Working with the 'task_finder' layer
Overview
The task_finder layer is supposed to do one thing - find "interesting" tasks. Currently, "interesting" is defined by matching either a pathname or pid. There are plans to add wildcard matching at some point in the future to the pathnames.
The task_finder layer is supposed to find "interesting" tasks for callers - what the caller does with the task after that is up to the caller.
Setup
First, callers need to declare a stap_task_finder_target for each process you are interested in. The stap_task_finder_target structure is defined at the top of task_finder.c, and it looks like this:
struct stap_task_finder_target { /* private: */ struct list_head list; /* __stp_task_finder_list linkage */ struct list_head callback_list_head; struct list_head callback_list; struct utrace_engine_ops ops; size_t pathlen; int engine_attached; unsigned mmap_events:1; unsigned munmap_events:1; unsigned mprotect_events:1; /* public: */ pid_t pid; const char *procname; stap_task_finder_callback callback; stap_task_finder_mmap_callback mmap_callback; stap_task_finder_munmap_callback munmap_callback; stap_task_finder_mprotect_callback mprotect_callback; };
Callers are interested in the 6 public fields. If you are interested in a particular pid, set procname to NULL and the pid to the correct pid. If you are interested in a path, set procname appropriately and set pid to 0. In both cases you'll need to define callback routines for the task_finder_target.
Once the stap_task_finder_target has been defined, you should pass it to a call to stap_register_task_finder_target().
Once all the stap_task_finder_target's have been registered, stap_start_task_finder() should be called to start everything up.
Operation
1. stap_task_finder_callback
When the task_finder layer finds an interesting thread, it will call the associated callback. The callback signature for an executable (ET_EXEC) target looks like this:
typedef int (*stap_task_finder_callback)(struct stap_task_finder_target *tgt, struct task_struct *tsk, int register_p, int process_p, );
Here's a description of each parameter.
'tgt' is the associated stap_task_finder_target structure you passed to stap_register_task_finder_target()
'tsk' identifies the interesting thread
'register_p' will be set to 1 if the task_finder has found a match. At this point the caller can do some setup on the thread, such as attaching a utrace engine, attaching a uprobe, etc. 'register_p' will be set to 0 if the thread is going away and the caller needs to cleanup anything attached to the thread.
'process_p' will be set to 1 if the thread is a process, 0 otherwise.
2. stap_task_finder_mmap_callback
The task finder has one path associated with a task and that path should be an executable, therefore procname should be the executable path not the shared object path. The callback signature for a shared object (ET_DYN) target looks like this:
typedef int (*stap_task_finder_mmap_callback)(structstap_task_finder_target *tgt, struct task_struct *tsk, char *path struct dentry *dentry unsigned long addr, unsigned long length, unsigned long offset, unsigned long vm_flags, );
Here's a description of each parameter.
'tgt' is the associated stap_task_finder_target structure you passed to stap_register_task_finder_target()
'tsk' identifies the interesting thread
'path' is the pathname of the shared object that is being mmaped.
'dentry' is a pointer to the directory entry (dentry) struct.
'addr' is the vma start address where the object is being mmapped
'length' is the length of the mmapped memory
'offset' is the page offset.
'vm_flags' are the flags, defined in mm.h.
3. stap_task_finder_munmap_callback
When the task_finder layer finds an interesting thread, it will call the associated callback. The callback signature for a shared object (ET_DYN) target being munmapped looks like this:
typedef int(*stap_task_finder_munmap_callback( structstap_task_finder_target *tgt, struct task_struct *tsk, unsigned long addr, unsigned long length, );
See stap_task_finder_mmap_callback for a description of each parameter.
Shutdown
At systemtap shutdown time, stap_stop_task_finder() must be called to allow the task_finder layer to disconnect from all the threads on the system.
Implementation
Internally the task_finder layer works by attaching a utrace engine to watch UTRACE_EVENT(CLONE) and UTRACE_EVENT(EXEC) events to every non-kernel thread in the system. It looks at existing threads and monitors all new threads (by following the CLONE/EXEC events), looking for pid/path matches. When it finds a match, it notifies the upper layer by calling the callback (and attaches a UTRACE_EVENT(DEATH) engine). When the matched thread dies, it notifies the upper layer by calling the callback.