From df61ffd1451723231b70401dbf7d895baa2f1081 Mon Sep 17 00:00:00 2001 From: David Smith Date: Thu, 1 Oct 2009 13:07:12 -0500 Subject: Fixed grapher build. * systemtap.spec: Updated grapher's buildreqs and file list. --- systemtap.spec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/systemtap.spec b/systemtap.spec index 3d05f8e1..a9865e2c 100644 --- a/systemtap.spec +++ b/systemtap.spec @@ -64,6 +64,7 @@ BuildRequires: xmlto /usr/share/xmlto/format/fo/pdf %if %{with_grapher} BuildRequires: gtkmm24-devel >= 2.8 +BuildRequires: libglademm24-devel >= 2.6.7 %endif %description @@ -388,6 +389,7 @@ exit 0 %files grapher %defattr(-,root,root) %{_bindir}/stapgraph +%{_datadir}/%{name}/*.glade %endif -- cgit From ae06d951b9775c39b92913417c902eae0775e4b6 Mon Sep 17 00:00:00 2001 From: Mark Wielaard Date: Fri, 2 Oct 2009 00:28:46 +0200 Subject: PR10678 vta-gcc: module debuginfo: relocation refers to undefined symbol libdwfl tries to resolve all relocations in a module debuginfo file and if it cannot find a symbol used in a relocation it will fail when dwfl_module_getdwarf() is called. So we must make sure all possible dependencies of the module are also in the dwfl. We do this by trying to find and parse the modules.dep file and insert all dependencies into the dwfl. * setupdwfl.cxx (elfutils_kernel_path): Lift from setup_dwfl_kernel and make static. (is_comma_dash): New function. (modname_from_path): Likewise. (setup_mod_deps): Likewise. (setup_dwfl_report_kernel_p): Call setup_mod_deps(). * testsuite/buildok/pr10678.stp: New test. --- setupdwfl.cxx | 108 +++++++++++++++++++++++++++++++++++++++++- testsuite/buildok/pr10678.stp | 4 ++ 2 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 testsuite/buildok/pr10678.stp diff --git a/setupdwfl.cxx b/setupdwfl.cxx index 554f4e54..32274615 100644 --- a/setupdwfl.cxx +++ b/setupdwfl.cxx @@ -13,6 +13,10 @@ #include "dwflpp.h" #include "session.h" +#include +#include +#include +#include #include #include @@ -77,6 +81,107 @@ static set user_modset; // symbols in vmlinux and/or other modules they depend on. See PR10678. static const bool setup_all_deps = true; +// Where to find the kernel (and the Modules.dep file). Setup in +// setup_dwfl_kernel(), used by dwfl_linux_kernel_report_offline() and +// setup_mod_deps(). +static string elfutils_kernel_path; + +static bool is_comma_dash(const char c) { return (c == ',' || c == '-'); } + +// The module name is the basename (without the extension) of the +// module path, with ',' and '-' replaced by '_'. +static string +modname_from_path(const string &path) +{ + size_t dot = path.rfind('.'); + size_t slash = path.rfind('/'); + if (dot == string::npos || slash == string::npos || dot < slash) + return ""; + string name = path.substr(slash + 1, dot - slash - 1); + replace_if(name.begin(), name.end(), is_comma_dash, '_'); + return name; +} + +// Try to parse modules.dep file, +// Simple format: module path (either full or relative), colon, +// (possibly empty) space delimited list of module (path) +// dependencies. +static void +setup_mod_deps() +{ + string modulesdep; + ifstream in; + string l; + + if (elfutils_kernel_path[0] == '/') + { + modulesdep = elfutils_kernel_path; + modulesdep += "/modules.dep"; + } + else + { + modulesdep = "/lib/modules/"; + modulesdep += elfutils_kernel_path; + modulesdep += "/modules.dep"; + } + in.open(modulesdep.c_str()); + if (in.fail ()) + return; + + while (getline (in, l)) + { + size_t off = l.find (':'); + if (off != string::npos) + { + string modpath, modname; + modpath = l.substr (0, off); + modname = modname_from_path (modpath); + if (modname == "") + continue; + + bool dep_needed; + if (offline_search_modname != NULL) + { + if (dwflpp::name_has_wildcard (offline_search_modname)) + { + dep_needed = !fnmatch (offline_search_modname, + modname.c_str (), 0); + if (dep_needed) + offline_search_names.insert (modname); + } + else + { + dep_needed = ! strcmp(modname.c_str (), + offline_search_modname); + if (dep_needed) + offline_search_names.insert (modname); + } + } + else + dep_needed = (offline_search_names.find (modname) + != offline_search_names.end ()); + + if (! dep_needed) + continue; + + string depstring = l.substr (off + 1); + if (depstring.size () > 0) + { + stringstream ss (depstring); + string deppath; + while (ss >> deppath) + offline_search_names.insert (modname_from_path(deppath)); + + } + } + } + + // We always want kernel (needed in list so size checks match). + // Everything needed now stored in offline_search_names. + offline_search_names.insert ("kernel"); + offline_search_modname = NULL; +} + // Set up our offline search for kernel modules. We don't want the // offline search iteration to do a complete search of the kernel // build tree, since that's wasteful, so create a predicate that @@ -101,6 +206,8 @@ setup_dwfl_report_kernel_p(const char* modname, const char* filename) || (offline_search_names.size() == 1 && *offline_search_names.begin() == "kernel")) setup_dwfl_done = true; + else + setup_mod_deps(); offline_modules_found++; return 1; @@ -162,7 +269,6 @@ setup_dwfl_kernel (unsigned *modules_found, systemtap_session &s) // no way to set the dwfl_callback.debuginfo_path and always // passs the plain kernel_release here. So instead we have to // hard-code this magic here. - string elfutils_kernel_path; if (s.kernel_build_tree == string("/lib/modules/" + s.kernel_release + "/build")) diff --git a/testsuite/buildok/pr10678.stp b/testsuite/buildok/pr10678.stp new file mode 100644 index 00000000..4ce8fb99 --- /dev/null +++ b/testsuite/buildok/pr10678.stp @@ -0,0 +1,4 @@ +#! stap -p4 + +# The ne2k_pci module dwarf refers to both kernel and 8390 module symbols +probe module("ne2k_pci").function("ne2k_pci_open") { log($$parms); } \ No newline at end of file -- cgit From 20561343a0f3bf420852885e070ad150a8be04ec Mon Sep 17 00:00:00 2001 From: Mark Wielaard Date: Fri, 2 Oct 2009 17:07:10 +0200 Subject: loc2c discontiguify loops for not small enough loc_constant. * loc2c.c (loc_constant): Add offset to size after creating piece in loc_constant loop. --- loc2c.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/loc2c.c b/loc2c.c index 14aaf53e..9a2e475f 100644 --- a/loc2c.c +++ b/loc2c.c @@ -1460,6 +1460,8 @@ discontiguify (struct obstack *pool, int indent, struct location *loc, piece->constant_block = loc->constant_block + offset; add (piece); + + offset += size; } break; -- cgit From 431716d37421f387479cf142634b90267119d188 Mon Sep 17 00:00:00 2001 From: Dave Brolley Date: Fri, 2 Oct 2009 16:20:31 -0400 Subject: Fix stap-server bug. --unprivileged not processed if processed as first argument. --- stap-server | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stap-server b/stap-server index 51abf62f..d99eec2d 100755 --- a/stap-server +++ b/stap-server @@ -172,7 +172,7 @@ function read_data_file { # parse all options in order to discover the ones we're interested in. function parse_options { # We need to know in advance if --unprivileged was specified. - all_options="$@"" " + all_options=" ""$@"" " token=`expr "$all_options" : '.* \(--unprivileged\) .*'` if test "X$token" = "X--unprivileged"; then unprivileged=1 -- cgit From a8f5a3bf344f9b014c5adf8b5eada10d09f31219 Mon Sep 17 00:00:00 2001 From: "Frank Ch. Eigler" Date: Fri, 2 Oct 2009 16:24:00 -0400 Subject: indent tapset: make O(1) rather than O(n) * tapset/indent.stp (_generic_indent): Use "%-*s" instead of string concatenation loop. --- tapset/indent.stp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tapset/indent.stp b/tapset/indent.stp index 1dbbebd4..2f850bfd 100644 --- a/tapset/indent.stp +++ b/tapset/indent.stp @@ -9,11 +9,7 @@ function _generic_indent (idx, desc, delta) x = _indent_counters[idx] + (delta > 0 ? delta : 0) _indent_counters[idx] += delta - r = sprintf("%6d %s:", (ts - _indent_timestamps[idx]), desc) - - for (i=1; i0 ? x-1 : 0), "") } /** -- cgit From ae3072f460693e85962556bf0529a729c7d97bf6 Mon Sep 17 00:00:00 2001 From: Kiran Prakesh Date: Thu, 1 Oct 2009 22:39:32 +0530 Subject: Scheduler Tapset based on kernel tracepoints This patch adds kernel tracepoints based probes to the scheduler tapset along with the testcase, scheduler-test-tracepoints.stp and an example script, sched_switch.stp. Signed-off-by: Kiran Prakash Signed-off-by: Josh Stone --- tapset/scheduler.stp | 353 ++++++++++++++++++--- testsuite/buildok/scheduler-test-tracepoints.stp | 51 +++ .../systemtap.examples/profiling/sched_switch.meta | 14 + .../systemtap.examples/profiling/sched_switch.stp | 62 ++++ 4 files changed, 429 insertions(+), 51 deletions(-) create mode 100644 testsuite/buildok/scheduler-test-tracepoints.stp create mode 100644 testsuite/systemtap.examples/profiling/sched_switch.meta create mode 100644 testsuite/systemtap.examples/profiling/sched_switch.stp diff --git a/tapset/scheduler.stp b/tapset/scheduler.stp index 3c3d504e..b1911ac2 100644 --- a/tapset/scheduler.stp +++ b/tapset/scheduler.stp @@ -20,7 +20,7 @@ function __is_idle:long() %} -/* probe scheduler.cpu_off +/** probe scheduler.cpu_off * * Fires when a process is about to stop running on a cpu. * @@ -41,7 +41,7 @@ probe scheduler.cpu_off } -/* probe scheduler.cpu_on +/** probe scheduler.cpu_on * * Fires when a process is beginning execution on a cpu. * @@ -76,26 +76,6 @@ probe scheduler.tick = kernel.function("scheduler_tick") idle = __is_idle() } - -/* probe scheduler.migrate - * - * Fires whenever a task is moved to a different cpu's runqueue. - * - * Context: - * Unknown (sometimes migration thread, sometimes cpu_to) - * - * Arguments: - * task - the process that is being migrated - * cpu_from - the cpu that is losing the task - * cpu_to - the cpu that is claiming the task - */ -probe scheduler.migrate = kernel.function("pull_task")? { - task = $p - cpu_from = task_cpu($p) /*thread_info renamed to stack since 2.6.22*/ - cpu_to = $this_cpu -} - - /* probe scheduler.balance * * Fires when a cpu attempts to find more work. @@ -107,43 +87,314 @@ probe scheduler.migrate = kernel.function("pull_task")? { probe scheduler.balance = kernel.function("idle_balance")? {} -/* probe scheduler.ctxswitch - * - * Fires when there is a context switch +/** + * probe scheduler.ctxswitch - Fires when there is a context switch. Currently + * systemTap can't access arguments of inline + * functions. So we choose to probe __switch_to instead + * of context_switch() + * @prev_pid: The pid of the process to be switched out + * @next_pid: The pid of the process to be switched in + * @prev_tid: The tid of the process to be switched out + * @next_tid: The tid of the process to be switched in + * @prev_task_name: The name of the process to be switched out + * @next_task_name: The name of the process to be switched in + * @prev_priority: The priority of the process to be switched out + * @next_priority: The priority of the process to be switched in + * @prevtsk_state: the state of the process to be switched out + * @nexttsk_state: the state of the process to be switched in + */ - * Currently systemTap can't access arguments of inline - * functions. So we choose to probe __switch_to instead - * of context_switch() +probe __scheduler.ctxswitch.tp = kernel.trace("sched_switch") +{ + next_pid = $next->tgid + next_tid = $next->pid + next_task = $next + next_task_name = task_execname($next) + nexttsk_state = $next->state + next_priority = $next->prio + prev_priority = $prev->prio + prev_pid = $prev->tgid + prev_tid = $prev->pid + prev_task = $prev + prev_task_name = task_execname($prev) + prevtsk_state = $prev->state +} - * Arguments: - * prev_pid: The pid of the process to be switched out - * next_pid: The pid of the process to be switched in - * prevtsk_state: the state of the process to be switched out - */ -probe scheduler.ctxswitch = +probe __scheduler.ctxswitch.kp = %( arch != "x86_64" && arch != "ia64" %? - kernel.trace("sched_switch") !, kernel.function("__switch_to") + kernel.function("__switch_to") %: - kernel.trace("sched_switch") !, kernel.function("context_switch") + kernel.function("context_switch") %) { %( arch == "powerpc" %? - prev_pid = $prev->pid - next_pid = $new->pid - prev_task = $prev - next_task = $new - prevtsk_state = $prev->state + prev_pid = $prev->tgid + next_pid = $new->tgid + prev_tid = $prev->pid + next_tid = $new->pid + prev_task = $prev + next_task = $new + next_priority = $new->prio + prev_priority = $prev->prio + prev_task_name = task_execname($prev) + next_task_name = task_execname($new) + prevtsk_state = $prev->state + nexttsk_state = $new->state + %: %( arch == "x86_64" || arch == "ia64" %? - prev_pid = $prev->pid - next_pid = $next->pid - prev_task = $prev - next_task = $next - prevtsk_state = $prev->state + prev_pid = $prev->tgid + next_pid = $next->tgid + prev_tid = $prev->pid + next_tid = $next->pid + prev_task = $prev + next_task = $next + next_priority = $next->prio + prev_priority = $prev->prio + prev_task_name = task_execname($prev) + next_task_name = task_execname($next) + prevtsk_state = $prev->state + nexttsk_state = $next->state %: - prev_pid = $prev_p->pid - next_pid = $next_p->pid - prev_task = $prev_p - next_task = $next_p - prevtsk_state = $prev_p->state + prev_pid = $prev_p->tgid + next_pid = $next_p->tgid + prev_tid = $prev_p->pid + next_tid = $next_p->pid + prev_task = $prev_p + next_task = $next_p + next_priority = $next_p->prio + prev_priority = $prev_p->prio + prev_task_name = task_execname($prev_p) + next_task_name = task_execname($next_p) + prevtsk_state = $prev_p->state + nexttsk_state = $next_p->state %) %) } + +probe scheduler.ctxswitch + = __scheduler.ctxswitch.tp !, __scheduler.ctxswitch.kp +{} + + +/** + * probe scheduler.kthread_stop - Fires when a thread created by kthread_create is stopped. + * @thread_pid: pid of the thread being stopped. + * @thread_priority: priority of the thread. + */ +probe __scheduler.kthread_stop.kp = kernel.function("kthread_stop") +{ + thread_pid = $k->tgid + thread_priority = $k->priority +} +probe __scheduler.kthread_stop.tp = kernel.trace("sched_kthread_stop") +{ + thread_pid = $t->tgid + thread_priority = $t->prio +} +probe scheduler.kthread_stop + = __scheduler.kthread_stop.tp !, + __scheduler.kthread_stop.kp +{} + + +/** + * probe scheduler.kthread_stop.return - Fires once the kthread is stopped and gets the return value + * @return_value: return value after stopping the thread. + */ + +probe __scheduler.kthread_stop.return.kp = kernel.function("kthread_stop").return +{ + return_value = $k->exit_code +} +probe __scheduler.kthread_stop.return.tp = kernel.trace("sched_kthread_stop_ret") +{ + return_value = $ret +} + +probe scheduler.kthread_stop.return + = __scheduler.kthread_stop.return.tp !, + __scheduler.kthread_stop.return.kp +{} + +/** + * probe scheduler.wait_task - Fires when waiting on a task to unschedule. + * It waits till the task becomes inactive. + * @task_pid: pid of the task the scheduler is waiting on. + * @task_priority: priority of the task + */ + +probe scheduler.wait_task + = kernel.trace("sched_wait_task") !, + kernel.function("wait_task_inactive") +{ + task_pid = $p->tgid + task_priority = $p->prio +} + +/** + * probe scheduler.wakeup - Fires when a task is woken up + * @task_pid: pid of the task being woken up + * @task_priority: priority of the task being woken up + * @task_cpu: cpu of the task being woken up + * @task_state: state of the task being woken up + * @task_tid: tid of the task being woken up + */ + +probe scheduler.wakeup + = kernel.trace("sched_wakeup") !, + kernel.function("try_to_wake_up") +{ + task = $p + task_pid = $p->tgid + task_tid = $p->pid + task_priority = $p->prio + task_cpu = task_cpu($p) + task_state = task_state($p) +} + +/** + * probe scheduler.wakeup_new - Fires when a newly created task is woken up for the first time + * @task_pid: pid of the new task woken up + * @task_priority: priority of the new task + * @task_tid: tid of the new task woken up + * @task_state: state of the task woken up + * @task_cpu: cpu of the task woken up + */ +probe scheduler.wakeup_new + = kernel.trace("sched_wakeup_new") !, + kernel.function("wake_up_new_task") +{ + task_pid = $p->tgid + task_priority = $p->prio + task_cpu = task_cpu($p) + task_state = task_state($p) + task = $p + task_tid = $p->pid +} + +/** + * probe scheduler.migrate - Traces the migration of the tasks across cpus by the scheduler. + * @task: the process that is being migrated. + * @pid: pid of the task being migrated. + * @priority: priority of the task being migrated. + * @cpu_from: the original cpu + * @cpu_to: the destination cpu + */ +probe __scheduler.migrate.kp1 = kernel.function("pull_task") +{ + cpu_to = $this_cpu +} +probe __scheduler.migrate.kp = kernel.function("set_task_cpu") +{ + cpu_to = $new_cpu +} +probe __scheduler.migrate.tp = kernel.trace("sched_migrate_task") +{ + cpu_to = $dest_cpu +} +probe scheduler.migrate + = __scheduler.migrate.tp !, + __scheduler.migrate.kp !, + __scheduler.migrate.kp1 +{ + task = $p + pid = $p->tgid + priority = $p->prio + cpu_from = task_cpu($p) +} +/** + * probe scheduler.process_free - Traces the process of freeing up of a process + * @pid: PID of the process getting freed + * @priority: priority of the process getting freed + */ +probe __scheduler.process_free.kp = kernel.function("delayed_put_task_struct") +{ + pid = $tsk->tgid + priority = $tsk->prio +} +probe __scheduler.process_free.tp = kernel.trace("sched_process_free") +{ + pid = $p->tgid + priority = $p->prio +} +probe scheduler.process_free + = __scheduler.process_free.tp !, + __scheduler.process_free.kp +{} + +/** + * probe scheduler.process_exit - Fires when a process exits + * @pid: pid of the process exiting + * @priority: priority of the process exiting + */ +probe __scheduler.process_exit.kp = kernel.function("do_exit") +{ + pid = $tsk->tgid + priority = $tsk->priority +} +probe __scheduler.process_exit.tp = kernel.trace("sched_process_exit") +{ + pid = $p->tgid + priority = $p->prio +} + +probe scheduler.process_exit + = __scheduler.process_exit.tp !, + __scheduler.process_exit.kp +{} + +/** + * probe scheduler.process_wait - Fires when scheduler waits on a process + * @pid: PID of the process scheduler is waiting on + */ +probe __scheduler.process_wait.kp = kernel.function("do_wait") +{ + pid = $wo->wo_pid +} +probe __scheduler.process_wait.tp = kernel.trace("sched_process_wait") +{ + pid = $pid +} +probe scheduler.process_wait + = __scheduler.process_wait.tp !, + __scheduler.process_wait.kp +{} + +/** + * probe scheduler.process_fork - Probes the tracepoint for forking a process + * @parent_pid: PID of the parent process + * @child_pid: PID of the child process + */ +probe __scheduler.process_fork.kp = kernel.function("do_fork") +{ + parent_pid = $current->tgid + child_pid = $p->tgid +} +probe __scheduler.process_fork.tp = kernel.trace("sched_process_fork") +{ + parent_pid = $parent->tgid + child_pid = $child->tgid +} + +probe scheduler.process_fork + = __scheduler.process_fork.tp !, + __scheduler.process_fork.kp +{} +/** + * probe scheduler.signal_send - Probes the tracepoint for sending a signal + * @pid: pid of the process sending signal + * @signal_number: signal number + */ +probe __scheduler.signal_send.kp = kernel.function("__send_signal") +{ + pid = $t->tgid +} +probe __scheduler.signal_send.tp = kernel.trace("sched_signal_send") +{ + pid = $p->tgid +} +probe scheduler.signal_send + = __scheduler.signal_send.tp !, + __scheduler.signal_send.kp +{ + signal_number = $sig +} diff --git a/testsuite/buildok/scheduler-test-tracepoints.stp b/testsuite/buildok/scheduler-test-tracepoints.stp new file mode 100644 index 00000000..a660c367 --- /dev/null +++ b/testsuite/buildok/scheduler-test-tracepoints.stp @@ -0,0 +1,51 @@ +#! stap -up4 + +//Tests if all probes in the scheduler tapset are resolvable. + +probe scheduler.kthread_stop { + printf("pid = %d, priority = %d\n", thread_pid, thread_priority); +} + +probe scheduler.kthread_stop.return { + printf("return value = %d\n", return_value); +} + +probe scheduler.wait_task { + printf("pid = %d, priority = %d\n", task_pid, task_priority); +} + +probe scheduler.wakeup { + printf("pid = %d, priority = %d\n, state = %d, cpu = %d, tid = %d\n",task_pid, task_priority, task_state, task_cpu, task_tid); +} + +probe scheduler.wakeup_new { + printf("pid = %d, priority = %d, state = %d, cpu = %d, tid = %d\n", task_pid, task_priority, task_state, task_cpu, task_tid); +} + +probe scheduler.ctxswitch { + printf("prev_pid = %d, prev_priority = %d, prev_state = %d, prev_task_name = %s, prev_tid = %d, next_pid = %d, next_priority = %d, next_state = %d, next_task_name = %s, next_tid = %d\n", prev_pid, prev_priority, prevtsk_state, prev_task_name, prev_tid, next_pid, next_priority, nexttsk_state, next_task_name, next_tid); +} + +probe scheduler.migrate { + printf("pid = %d, priority = %d, original cpu = %d destination cpu = %d\n", pid, priority, cpu_from, cpu_to); +} + +probe scheduler.process_free { + printf("pid = %d, priority = %d\n", pid, priority); +} + +probe scheduler.process_exit { + printf("pid = %d, priority = %d\n", pid, priority); +} + +probe scheduler.process_wait { + printf("pid = %d\n", pid); +} + +probe scheduler.process_fork { + printf("parent pid = %d, child pid = %d\n", parent_pid, child_pid); +} + +probe scheduler.signal_send { + printf("pid = %d, signal = %d\n", pid, signal_number); +} diff --git a/testsuite/systemtap.examples/profiling/sched_switch.meta b/testsuite/systemtap.examples/profiling/sched_switch.meta new file mode 100644 index 00000000..8f1a2858 --- /dev/null +++ b/testsuite/systemtap.examples/profiling/sched_switch.meta @@ -0,0 +1,14 @@ +title: Display the task switches happeningt the scheduler +name: sched_switch.stp +version: 1.0 +author: kiran +keywords: profiling functions +subsystem: kernel +status: production +exit: user-controlled +output: sorted-list on-exit +scope: system-wide +description: The sched_switch.stp script takes two arguments, first argument can be "pid" or "name" to indicate what is being passed as second argument. The script will trace the process based on pid/name and print the scheduler switches happening with the process. If no arguments are passed, it displays all the scheduler switches. This can be used to understand which tasks scheduler the current process being traced, out and when it gets scheduled in again. +test_check: stap -p4 sched_switch.stp +test_installcheck: stap sched_switch.stp -c "sleep 1" + diff --git a/testsuite/systemtap.examples/profiling/sched_switch.stp b/testsuite/systemtap.examples/profiling/sched_switch.stp new file mode 100644 index 00000000..24973526 --- /dev/null +++ b/testsuite/systemtap.examples/profiling/sched_switch.stp @@ -0,0 +1,62 @@ +/* This script works similar to ftrace's sched_switch. It displays a list of + * processes which get switched in and out of the scheduler. The format of display + * is PROCESS_NAME PROCESS_PID CPU TIMESTAMP PID: PRIORITY: PROCESS STATE ->/+ + * NEXT_PID : NEXT_PRIORITY: NEXT_STATE NEXT_PROCESS_NAME + * -> indicates that prev process is scheduled out and the next process is + * scheduled in. + * + indicates that prev process has woken up the next process. + * The usage is sched_switch.stp <"pid"/"name"> pid/name + */ + +function state_calc(state) { + if(state == 0) + status = "R" + if(state == 1) + status = "S" + if(state == 2) + status = "D" + if(state == 4) + status = "T" + if(state == 8) + status = "T" + if(state == 16) + status = "Z" + if(state == 32) + status = "EXIT_DEAD" + return status +} +probe scheduler.wakeup +{ + %( $# == 2 %? + + if(@1 == "pid") + if (task_pid != $2 && pid() != $2) + next + if(@1 == "name") + if (task_execname(task) != @2 && execname() != @2) + next + + %) + + printf("%-16s%5d%5d%d:%d:%s + %d:%d:%s %16s\n", + execname(), task_cpu(task), gettimeofday_ns(), + pid(), task_prio(task_current()), state_calc(task_state(task_current())), + task_pid(task), task_prio(task), state_calc(task_state(task)), + task_execname(task)) +} +probe scheduler.ctxswitch +{ + %( $# == 2 %? + + if(@1 == "pid") + if (next_pid != $2 && prev_pid != $2) + next + if(@1 == "name") + if (prev_task_name != @2 && next_task_name != @2) + next + %) + + printf("%-16s%5d%5d%d:%d:%s ==> %d:%d:%s %16s\n",prev_task_name, + task_cpu(prev_task),gettimeofday_ns(),prev_pid,prev_priority,state_calc(prevtsk_state),next_pid, + next_priority,state_calc(nexttsk_state),next_task_name) +} -- cgit From 0449af0365f19c246f6ce09dd93118a597f78949 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 2 Oct 2009 16:42:44 -0700 Subject: Updates samples index for sched_switch --- testsuite/systemtap.examples/index.html | 3 +++ testsuite/systemtap.examples/index.txt | 12 +++++++++++ testsuite/systemtap.examples/keyword-index.html | 6 ++++++ testsuite/systemtap.examples/keyword-index.txt | 24 ++++++++++++++++++++++ .../systemtap.examples/profiling/sched_switch.meta | 6 +++--- .../systemtap.examples/profiling/sched_switch.stp | 1 + 6 files changed, 49 insertions(+), 3 deletions(-) mode change 100644 => 100755 testsuite/systemtap.examples/profiling/sched_switch.stp diff --git a/testsuite/systemtap.examples/index.html b/testsuite/systemtap.examples/index.html index ba0d0fd7..66118bfc 100644 --- a/testsuite/systemtap.examples/index.html +++ b/testsuite/systemtap.examples/index.html @@ -196,6 +196,9 @@ keywords: SYSCALL profiling/functioncallcount.stp - Count Times Functions Called
keywords: PROFILING FUNCTIONS

The functioncallcount.stp script takes one argument, a list of functions to probe. The script will run and count the number of times that each of the functions on the list is called. On exit the script will print a sorted list from most frequently to least frequently called function.

+
  • profiling/sched_switch.stp - Display the task switches happening in the scheduler
    +keywords: PROFILING FUNCTIONS
    +

    The sched_switch.stp script takes two arguments, first argument can be "pid" or "name" to indicate what is being passed as second argument. The script will trace the process based on pid/name and print the scheduler switches happening with the process. If no arguments are passed, it displays all the scheduler switches. This can be used to understand which tasks schedule out the current process being traced, and when it gets scheduled in again.

  • profiling/thread-times.stp - Profile kernel functions
    keywords: PROFILING

    The thread-times.stp script sets up time-based sampling. Every five seconds it prints out a sorted list with the top twenty processes with samples broken down into percentage total time spent in user-space and kernel-space.

  • diff --git a/testsuite/systemtap.examples/index.txt b/testsuite/systemtap.examples/index.txt index 3d0495f5..cb2b10d3 100644 --- a/testsuite/systemtap.examples/index.txt +++ b/testsuite/systemtap.examples/index.txt @@ -488,6 +488,18 @@ keywords: profiling functions called function. +profiling/sched_switch.stp - Display the task switches happening in the scheduler +keywords: profiling functions + + The sched_switch.stp script takes two arguments, first argument can + be "pid" or "name" to indicate what is being passed as second + argument. The script will trace the process based on pid/name and + print the scheduler switches happening with the process. If no + arguments are passed, it displays all the scheduler switches. This + can be used to understand which tasks schedule out the current + process being traced, and when it gets scheduled in again. + + profiling/thread-times.stp - Profile kernel functions keywords: profiling diff --git a/testsuite/systemtap.examples/keyword-index.html b/testsuite/systemtap.examples/keyword-index.html index 1a68a9f0..2aad9adf 100644 --- a/testsuite/systemtap.examples/keyword-index.html +++ b/testsuite/systemtap.examples/keyword-index.html @@ -120,6 +120,9 @@ keywords: NETWORK profiling/functioncallcount.stp - Count Times Functions Called
    keywords: PROFILING FUNCTIONS

    The functioncallcount.stp script takes one argument, a list of functions to probe. The script will run and count the number of times that each of the functions on the list is called. On exit the script will print a sorted list from most frequently to least frequently called function.

    +
  • profiling/sched_switch.stp - Display the task switches happening in the scheduler
    +keywords: PROFILING FUNCTIONS
    +

    The sched_switch.stp script takes two arguments, first argument can be "pid" or "name" to indicate what is being passed as second argument. The script will trace the process based on pid/name and print the scheduler switches happening with the process. If no arguments are passed, it displays all the scheduler switches. This can be used to understand which tasks schedule out the current process being traced, and when it gets scheduled in again.

  • FUTEX

      @@ -300,6 +303,9 @@ keywords: PROFILING
    • profiling/functioncallcount.stp - Count Times Functions Called
      keywords: PROFILING FUNCTIONS

      The functioncallcount.stp script takes one argument, a list of functions to probe. The script will run and count the number of times that each of the functions on the list is called. On exit the script will print a sorted list from most frequently to least frequently called function.

    • +
    • profiling/sched_switch.stp - Display the task switches happening in the scheduler
      +keywords: PROFILING FUNCTIONS
      +

      The sched_switch.stp script takes two arguments, first argument can be "pid" or "name" to indicate what is being passed as second argument. The script will trace the process based on pid/name and print the scheduler switches happening with the process. If no arguments are passed, it displays all the scheduler switches. This can be used to understand which tasks schedule out the current process being traced, and when it gets scheduled in again.

    • profiling/thread-times.stp - Profile kernel functions
      keywords: PROFILING

      The thread-times.stp script sets up time-based sampling. Every five seconds it prints out a sorted list with the top twenty processes with samples broken down into percentage total time spent in user-space and kernel-space.

    • diff --git a/testsuite/systemtap.examples/keyword-index.txt b/testsuite/systemtap.examples/keyword-index.txt index 056b553a..01661cf1 100644 --- a/testsuite/systemtap.examples/keyword-index.txt +++ b/testsuite/systemtap.examples/keyword-index.txt @@ -154,6 +154,18 @@ keywords: profiling functions called function. +profiling/sched_switch.stp - Display the task switches happening in the scheduler +keywords: profiling functions + + The sched_switch.stp script takes two arguments, first argument can + be "pid" or "name" to indicate what is being passed as second + argument. The script will trace the process based on pid/name and + print the scheduler switches happening with the process. If no + arguments are passed, it displays all the scheduler switches. This + can be used to understand which tasks schedule out the current + process being traced, and when it gets scheduled in again. + + = FUTEX = process/futexes.stp - System-Wide Futex Contention @@ -618,6 +630,18 @@ keywords: profiling functions called function. +profiling/sched_switch.stp - Display the task switches happening in the scheduler +keywords: profiling functions + + The sched_switch.stp script takes two arguments, first argument can + be "pid" or "name" to indicate what is being passed as second + argument. The script will trace the process based on pid/name and + print the scheduler switches happening with the process. If no + arguments are passed, it displays all the scheduler switches. This + can be used to understand which tasks schedule out the current + process being traced, and when it gets scheduled in again. + + profiling/thread-times.stp - Profile kernel functions keywords: profiling diff --git a/testsuite/systemtap.examples/profiling/sched_switch.meta b/testsuite/systemtap.examples/profiling/sched_switch.meta index 8f1a2858..b202a74c 100644 --- a/testsuite/systemtap.examples/profiling/sched_switch.meta +++ b/testsuite/systemtap.examples/profiling/sched_switch.meta @@ -1,4 +1,4 @@ -title: Display the task switches happeningt the scheduler +title: Display the task switches happening in the scheduler name: sched_switch.stp version: 1.0 author: kiran @@ -6,9 +6,9 @@ keywords: profiling functions subsystem: kernel status: production exit: user-controlled -output: sorted-list on-exit +output: trace scope: system-wide -description: The sched_switch.stp script takes two arguments, first argument can be "pid" or "name" to indicate what is being passed as second argument. The script will trace the process based on pid/name and print the scheduler switches happening with the process. If no arguments are passed, it displays all the scheduler switches. This can be used to understand which tasks scheduler the current process being traced, out and when it gets scheduled in again. +description: The sched_switch.stp script takes two arguments, first argument can be "pid" or "name" to indicate what is being passed as second argument. The script will trace the process based on pid/name and print the scheduler switches happening with the process. If no arguments are passed, it displays all the scheduler switches. This can be used to understand which tasks schedule out the current process being traced, and when it gets scheduled in again. test_check: stap -p4 sched_switch.stp test_installcheck: stap sched_switch.stp -c "sleep 1" diff --git a/testsuite/systemtap.examples/profiling/sched_switch.stp b/testsuite/systemtap.examples/profiling/sched_switch.stp old mode 100644 new mode 100755 index 24973526..1c1b18b7 --- a/testsuite/systemtap.examples/profiling/sched_switch.stp +++ b/testsuite/systemtap.examples/profiling/sched_switch.stp @@ -1,3 +1,4 @@ +#! /usr/bin/env stap /* This script works similar to ftrace's sched_switch. It displays a list of * processes which get switched in and out of the scheduler. The format of display * is PROCESS_NAME PROCESS_PID CPU TIMESTAMP PID: PRIORITY: PROCESS STATE ->/+ -- cgit From ee89e81c3f87c4eef2b677ed7e38092e3d2c5af9 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 2 Oct 2009 16:50:18 -0700 Subject: AUTHORS bump for Kiran --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 9da6c495..5ddbb59f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -27,6 +27,7 @@ Kai Meyer Keiichi KII Kent Sebastian Kevin Stafford +Kiran Prakesh Li Guanglei Lubomir Rintel Mahesh J Salgaonkar -- cgit From 73b5e9064aeb9d5b4862e8cf688f8048f9fce86a Mon Sep 17 00:00:00 2001 From: Mark Wielaard Date: Mon, 5 Oct 2009 09:05:29 +0200 Subject: Make sure loc2c declare_noncontig_union for different locs don't overlap. * loc2c.c (declare_noncontig_union): Name union u_pieces for loc_noncontiguous or u_const for loc_constant. (translate_base_store): Use u_pieces for loc_noncontiguous. (translate_base_fetch): Likewise or u_const for loc_constant. --- loc2c.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/loc2c.c b/loc2c.c index 9a2e475f..2a1d16e0 100644 --- a/loc2c.c +++ b/loc2c.c @@ -1528,7 +1528,17 @@ declare_noncontig_union (struct obstack *pool, int indent, obstack_printf (pool, "%*suint%" PRIu64 "_t whole;\n", indent * 2, "", loc->byte_size * 8); - obstack_printf (pool, "%*s} u;\n", --indent * 2, ""); + // Different loc types could be in the same syntactical scope, so + // should be named differently. + const char *uname; + if (loc->type == loc_noncontiguous) + uname = "u_pieces"; + else if (loc->type == loc_constant) + uname = "u_const"; + else + abort(); + + obstack_printf (pool, "%*s} %s;\n", --indent * 2, "", uname); loc = new_synthetic_loc (pool, *input, false); loc->type = loc_decl; @@ -1610,7 +1620,7 @@ translate_base_fetch (struct obstack *pool, int indent, declare_noncontig_union (pool, indent, input, *input); Dwarf_Word offset = 0; - char piece[sizeof "u.pieces.p" + 20] = "u.pieces.p"; + char piece[sizeof "u_pieces.pieces.p" + 20] = "u_pieces.pieces.p"; while (p != NULL) { struct location *newp = obstack_alloc (pool, sizeof *newp); @@ -1619,7 +1629,8 @@ translate_base_fetch (struct obstack *pool, int indent, (*input)->next = newp; *input = newp; - snprintf (&piece[sizeof "u.pieces.p" - 1], 20, "%" PRIu64, offset); + snprintf (&piece[sizeof "u_pieces.pieces.p" - 1], 20, + "%" PRIu64, offset); translate_base_fetch (pool, indent, p->byte_size, signed_p /* ? */, input, piece); (*input)->type = loc_fragment; @@ -1628,7 +1639,8 @@ translate_base_fetch (struct obstack *pool, int indent, p = p->next; } - obstack_printf (pool, "%*s%s = u.whole;\n", indent * 2, "", target); + obstack_printf (pool, "%*s%s = u_pieces.whole;\n", indent * 2, + "", target); } else if ((*input)->type == loc_constant) { @@ -1639,10 +1651,11 @@ translate_base_fetch (struct obstack *pool, int indent, declare_noncontig_union (pool, indent, input, *input); for (i = 0; i < byte_size; ++i) - obstack_printf (pool, "%*su.bytes[%zu] = %#x;\n", indent * 2, "", - i, constant_block[i]); + obstack_printf (pool, "%*su_const.bytes[%zu] = %#x;\n", indent * 2, + "", i, constant_block[i]); - obstack_printf (pool, "%*s%s = u.whole;\n", indent * 2, "", target); + obstack_printf (pool, "%*s%s = u_const.whole;\n", indent * 2, + "", target); } else switch (byte_size) @@ -1748,14 +1761,15 @@ translate_base_store (struct obstack *pool, int indent, Dwarf_Word byte_size, { declare_noncontig_union (pool, indent, input, store_loc); - obstack_printf (pool, "%*su.whole = %s;\n", indent * 2, "", rvalue); + obstack_printf (pool, "%*su_pieces.whole = %s;\n", indent * 2, + "", rvalue); struct location *loc = new_synthetic_loc (pool, *input, deref); loc->type = loc_fragment; (*input)->next = loc; *input = loc; Dwarf_Word offset = 0; - char piece[sizeof "u.pieces.p" + 20] = "u.pieces.p"; + char piece[sizeof "u_pieces.pieces.p" + 20] = "u_pieces.pieces.p"; struct location *p; for (p = store_loc->pieces; p != NULL; p = p->next) { @@ -1765,7 +1779,8 @@ translate_base_store (struct obstack *pool, int indent, Dwarf_Word byte_size, (*input)->next = newp; *input = newp; - snprintf (&piece[sizeof "u.pieces.p" - 1], 20, "%" PRIu64, offset); + snprintf (&piece[sizeof "u_pieces.pieces.p" - 1], 20, "%" PRIu64, + offset); translate_base_store (pool, indent, p->byte_size, input, *input, piece); (*input)->type = loc_fragment; -- cgit From 03ba05b65a2c555d3057006ef490d958bbf07df5 Mon Sep 17 00:00:00 2001 From: Mark Wielaard Date: Mon, 5 Oct 2009 09:11:59 +0200 Subject: Handle DW_AT_const_value as alternative to location description. * dwflpp.cxx (translate_location): Call c_translate_constant when attribute is DW_AT_const_value. (literal_stmt_for_local): Allow both DW_AT_location and DW_AT_const_value. --- dwflpp.cxx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/dwflpp.cxx b/dwflpp.cxx index c611dbe3..3356ad78 100644 --- a/dwflpp.cxx +++ b/dwflpp.cxx @@ -1654,6 +1654,14 @@ dwflpp::translate_location(struct obstack *pool, } #endif + /* There is no location expression, but a constant value instead. */ + if (dwarf_whatattr (attr) == DW_AT_const_value) + { + *tail = c_translate_constant (pool, &loc2c_error, this, + &loc2c_emit_address, 0, pc, attr); + return *tail; + } + Dwarf_Op *expr; size_t len; @@ -2216,7 +2224,8 @@ dwflpp::literal_stmt_for_local (vector& scopes, << "\n"; Dwarf_Attribute attr_mem; - if (dwarf_attr_integrate (&vardie, DW_AT_location, &attr_mem) == NULL) + if (dwarf_attr_integrate (&vardie, DW_AT_const_value, &attr_mem) == NULL + && dwarf_attr_integrate (&vardie, DW_AT_location, &attr_mem) == NULL) { throw semantic_error("failed to retrieve location " "attribute for local '" + local -- cgit From 3e9565715db9be1dd1719a33aa7d341c930aadda Mon Sep 17 00:00:00 2001 From: Mark Wielaard Date: Mon, 5 Oct 2009 09:14:49 +0200 Subject: Add tescase for DW_AT_const_value location descriptions. * testsuite/systemtap.base/const_value.c: New test. * testsuite/systemtap.base/const_value.exp: Likewise. * testsuite/systemtap.base/const_value.stp: Likewise. --- testsuite/systemtap.base/const_value.c | 31 +++++++++++++++++++++++++++++ testsuite/systemtap.base/const_value.exp | 34 ++++++++++++++++++++++++++++++++ testsuite/systemtap.base/const_value.stp | 6 ++++++ 3 files changed, 71 insertions(+) create mode 100644 testsuite/systemtap.base/const_value.c create mode 100644 testsuite/systemtap.base/const_value.exp create mode 100644 testsuite/systemtap.base/const_value.stp diff --git a/testsuite/systemtap.base/const_value.c b/testsuite/systemtap.base/const_value.c new file mode 100644 index 00000000..4ff82161 --- /dev/null +++ b/testsuite/systemtap.base/const_value.c @@ -0,0 +1,31 @@ +#include "sdt.h" + +struct foo +{ + const int i; + const long j; +}; + +typedef struct foo fooer; + +static int +bar (const int i, const long j) +{ + return i * j; +} + +// Because of PR10726 we don't want to get this function inlined. +// We do need -O2 to get the const_value encodings in dwarf. +static __attribute__((__noinline__)) int +func (int (*f) ()) +{ + const fooer baz = { .i = 2, .j = 21 }; + STAP_PROBE (test, constvalues); + return f(baz.i, baz.j); +} + +int +main (int argc, char *argv[], char *envp[]) +{ + return func (&bar) - 42; +} diff --git a/testsuite/systemtap.base/const_value.exp b/testsuite/systemtap.base/const_value.exp new file mode 100644 index 00000000..5d5ceaee --- /dev/null +++ b/testsuite/systemtap.base/const_value.exp @@ -0,0 +1,34 @@ +set test "const_value" +set ::result_string {f: bar +i: 2 +j: 21} + +set test_flags "additional_flags=-g" +# We need -O2 to get const_value encodings in dwarf. +set test_flags "$test_flags additional_flags=-O2" +set test_flags "$test_flags additional_flags=-I$srcdir/../includes/sys" + +set res [target_compile $srcdir/$subdir/$test.c $test.exe executable "$test_flags"] +if { $res != "" } { + verbose "target_compile failed: $res" 2 + fail "$test.c compile" + untested "$test" + return +} else { + pass "$test.c compile" +} + +# Test only when we are running an install test (can execute) and when gcc +# vta generated DW_AT_const_values for us. We are interested in both blocks +# and pure constant addresses (technically only the second was added by vta). +if {[installtest_p] && [uprobes_p]} { + set dw_at_c {DW_AT_const_value} + if {![catch {exec readelf --debug-dump=info $test.exe | grep "$dw_at_c"}]} { + stap_run2 $srcdir/$subdir/$test.stp -c ./$test.exe + } { + untested "$test (no-gcc-vta)" + } +} else { + untested "$test" +} +catch {exec rm -f $test.exe} diff --git a/testsuite/systemtap.base/const_value.stp b/testsuite/systemtap.base/const_value.stp new file mode 100644 index 00000000..109d3d46 --- /dev/null +++ b/testsuite/systemtap.base/const_value.stp @@ -0,0 +1,6 @@ +probe process("const_value.exe").mark("constvalues") +{ + printf("f: %s\n", usymname($f)); + printf("i: %d\n", $baz->i); + printf("j: %d\n", $baz->j); +} \ No newline at end of file -- cgit From 6e2d11625b991b4094c9f2cc04e30f8d00f711a6 Mon Sep 17 00:00:00 2001 From: Mark Wielaard Date: Mon, 5 Oct 2009 10:22:59 +0200 Subject: NEWS: Better gcc 4.5 DWARF support (kernel module deps, DW_AT_const_value). --- NEWS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/NEWS b/NEWS index f0187614..b8a42305 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,12 @@ - ftrace(msg:string) tapset function to send strings to the system-wide ftrace ring-buffer (if any). +- Better support for richer DWARF debuginfo output from GCC 4.5 + (variable tracking assignments). Kernel modules are now always resolved + against all their dependencies to find any info referring to missing + symbols. DW_AT_const_value is now supported when no DW_AT_location + is available. + * What's new in verson 1.0 - process().mark() probes now use an enabling semaphore to reduce the -- cgit From 6b517475576ac5f2209e5231d14d0b8aa0b6a4d5 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 5 Oct 2009 17:41:30 -0700 Subject: PR10726: Get the correct scope for statement(NUM) The problem in this bug is that our statement(NUM) lookup was only searching for the outermost function (not inlined) which contains the PC in question. When that PC happens to be the beginning of the function and also the beginning of an inline, the caching was using the wrong variable scope. The function/statement(NUM) lookup has been rewritten to bypass all of the CU and function iteration, and just go straight to a getscopes(pc) lookup, so it will now always use the innermost containing die for the variable scope. * tapsets.cxx (query_addr): New, short-circuit for numeric probes. (dwarf_query::query_module_dwarf): Route num probes to query_addr. (query_label): Assume now that we only need to handle _str probes. (query_dwarf_inline_instance): Ditto. (query_dwarf_func): Ditto. (query_cu): Ditto. --- tapsets.cxx | 447 +++++++++++++++++++++++++++++------------------------------- 1 file changed, 217 insertions(+), 230 deletions(-) diff --git a/tapsets.cxx b/tapsets.cxx index 4433af3f..6267f314 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -261,6 +261,7 @@ static const string TOK_TRACE("trace"); static const string TOK_LABEL("label"); static int query_cu (Dwarf_Die * cudie, void * arg); +static void query_addr(Dwarf_Addr addr, dwarf_query *q); // Can we handle this query with just symbol-table info? enum dbinfo_reqt @@ -731,19 +732,9 @@ dwarf_query::query_module_dwarf() // module("foo").statement(0xbeef), the address is relative // to the start of the module, so we seek the function // number plus the module's bias. - - Dwarf_Addr addr; - if (has_function_num) - addr = function_num_val; - else - addr = statement_num_val; - - // Translate to an actual symbol address. - addr = dw.literal_addr_to_sym_addr (addr); - - Dwarf_Die* cudie = dw.query_cu_containing_address(addr); - if (cudie) // address could be wildly out of range - query_cu(cudie, this); + Dwarf_Addr addr = has_function_num ? + function_num_val : statement_num_val; + query_addr(addr, this); } else { @@ -1145,6 +1136,111 @@ query_statement (string const & func, } } +static void +query_addr(Dwarf_Addr addr, dwarf_query *q) +{ + dwflpp &dw = q->dw; + + // Translate to and actual sumbol address. + addr = dw.literal_addr_to_sym_addr(addr); + + // First pick which CU contains this address + Dwarf_Die* cudie = dw.query_cu_containing_address(addr); + if (!cudie) // address could be wildly out of range + return; + dw.focus_on_cu(cudie); + + // Now compensate for the dw bias + addr -= dw.module_bias; + + // Per PR5787, we look up the scope die even for + // statement_num's, for blacklist sensitivity and $var + // resolution purposes. + + // Find the scopes containing this address + vector scopes = dw.getscopes(addr); + if (scopes.empty()) + return; + + // Look for the innermost containing function + Dwarf_Die *fnscope = NULL; + for (size_t i = 0; i < scopes.size(); ++i) + { + int tag = dwarf_tag(&scopes[i]); + if ((tag == DW_TAG_subprogram && !q->has_inline) || + (tag == DW_TAG_inlined_subroutine && + !q->has_call && !q->has_return)) + { + fnscope = &scopes[i]; + break; + } + } + if (!fnscope) + return; + dw.focus_on_function(fnscope); + + Dwarf_Die *scope = q->has_function_num ? fnscope : &scopes[0]; + + const char *file = dwarf_decl_file(fnscope); + int line; + dwarf_decl_line(fnscope, &line); + + // Function probes should reset the addr to the function entry + // and possibly perform prologue searching + if (q->has_function_num) + { + dw.die_entrypc(fnscope, &addr); + if (dwarf_tag(fnscope) == DW_TAG_subprogram && + (q->sess.prologue_searching || q->has_process)) // PR 6871 + { + func_info func; + func.die = *fnscope; + func.name = dw.function_name; + func.decl_file = file; + func.decl_line = line; + func.entrypc = addr; + + func_info_map_t funcs(1, func); + dw.resolve_prologue_endings (funcs); + if (funcs[0].prologue_end) + addr = funcs[0].prologue_end; + } + } + else + { + dwarf_line_t address_line(dwarf_getsrc_die(cudie, addr)); + if (address_line) + { + file = address_line.linesrc(); + line = address_line.lineno(); + } + + // Verify that a raw address matches the beginning of a + // statement. This is a somewhat lame check that the address + // is at the start of an assembly instruction. Mark probes are in the + // middle of a macro and thus not strictly at a statement beginning. + // Guru mode may override this check. + if (!q->has_mark && (!address_line || address_line.addr() != addr)) + { + stringstream msg; + msg << "address 0x" << hex << addr + << " does not match the beginning of a statement"; + if (address_line) + msg << " (try 0x" << hex << address_line.addr() << ")"; + else + msg << " (no line info found for '" << dw.cu_name() + << "', in module '" << dw.module_name << "')"; + if (! q->sess.guru_mode) + throw semantic_error(msg.str()); + else if (! q->sess.suppress_warnings) + q->sess.print_warning(msg.str()); + } + } + + // Build a probe at this point + query_statement(dw.function_name, file, line, scope, addr, q); +} + static void query_label (string const & func, char const * label, @@ -1154,12 +1250,13 @@ query_label (string const & func, Dwarf_Addr stmt_addr, dwarf_query * q) { + assert (q->has_statement_str || q->has_function_str); + size_t i = q->results.size(); // weed out functions whose decl_file isn't one of // the source files that we actually care about - if ((q->has_statement_str || q->has_function_str) && - q->spec_type != function_alone && + if (q->spec_type != function_alone && q->filtered_srcfiles.count(file) == 0) return; @@ -1316,36 +1413,29 @@ static int query_dwarf_inline_instance (Dwarf_Die * die, void * arg) { dwarf_query * q = static_cast(arg); - assert (!q->has_statement_num); + assert (q->has_statement_str || q->has_function_str); + assert (!q->has_call && !q->has_return); try { if (q->sess.verbose>2) - clog << "examining inline instance of " << q->dw.function_name << "\n"; - - if ((q->has_function_str && ! q->has_call) - || q->has_statement_str) - { - if (q->sess.verbose>2) - clog << "selected inline instance of " << q->dw.function_name - << "\n"; + clog << "selected inline instance of " << q->dw.function_name << "\n"; - Dwarf_Addr entrypc; - if (q->dw.die_entrypc (die, &entrypc)) - { - inline_instance_info inl; - inl.die = *die; - inl.name = q->dw.function_name; - inl.entrypc = entrypc; - q->dw.function_file (&inl.decl_file); - q->dw.function_line (&inl.decl_line); - - // make sure that this inline hasn't already - // been matched from a different CU - if (q->inline_dupes.insert(inl).second) - q->filtered_inlines.push_back(inl); - } - } + Dwarf_Addr entrypc; + if (q->dw.die_entrypc (die, &entrypc)) + { + inline_instance_info inl; + inl.die = *die; + inl.name = q->dw.function_name; + inl.entrypc = entrypc; + q->dw.function_file (&inl.decl_file); + q->dw.function_line (&inl.decl_line); + + // make sure that this inline hasn't already + // been matched from a different CU + if (q->inline_dupes.insert(inl).second) + q->filtered_inlines.push_back(inl); + } return DWARF_CB_OK; } catch (const semantic_error& e) @@ -1359,11 +1449,11 @@ static int query_dwarf_func (Dwarf_Die * func, base_query * bq) { dwarf_query * q = static_cast(bq); + assert (q->has_statement_str || q->has_function_str); // weed out functions whose decl_file isn't one of // the source files that we actually care about - if ((q->has_statement_str || q->has_function_str) && - q->spec_type != function_alone && + if (q->spec_type != function_alone && q->filtered_srcfiles.count(dwarf_decl_file(func)?:"") == 0) return DWARF_CB_OK; @@ -1382,9 +1472,7 @@ query_dwarf_func (Dwarf_Die * func, base_query * bq) !q->alias_dupes.insert(addr).second) return DWARF_CB_OK; - if (q->dw.func_is_inline () - && (! q->has_call) && (! q->has_return) - && (q->has_statement_str || q->has_function_str)) + if (q->dw.func_is_inline () && (! q->has_call) && (! q->has_return)) { if (q->sess.verbose>3) clog << "checking instances of inline " << q->dw.function_name @@ -1393,67 +1481,22 @@ query_dwarf_func (Dwarf_Die * func, base_query * bq) } else if (!q->dw.func_is_inline () && (! q->has_inline)) { - bool record_this_function = false; + if (q->sess.verbose>2) + clog << "selected function " << q->dw.function_name << "\n"; - if (q->has_statement_str || q->has_function_str) - { - record_this_function = true; - } - else if (q->has_function_num || q->has_statement_num) - { - Dwarf_Addr query_addr = - (q->has_function_num ? q->function_num_val : - q->has_statement_num ? q->statement_num_val : - (assert(0) , 0)); - Dwarf_Die d; - q->dw.function_die (&d); - - // Translate literal address to symbol address, then - // compensate for dw bias. - query_addr = q->dw.literal_addr_to_sym_addr(query_addr); - query_addr -= q->dw.module_bias; - - if (q->dw.die_has_pc (d, query_addr)) - record_this_function = true; - } + func_info func; + q->dw.function_die (&func.die); + func.name = q->dw.function_name; + q->dw.function_file (&func.decl_file); + q->dw.function_line (&func.decl_line); - if (record_this_function) - { - if (q->sess.verbose>2) - clog << "selected function " << q->dw.function_name << "\n"; - - func_info func; - q->dw.function_die (&func.die); - func.name = q->dw.function_name; - q->dw.function_file (&func.decl_file); - q->dw.function_line (&func.decl_line); - - if (q->has_function_num || q->has_function_str || q->has_statement_str) - { - Dwarf_Addr entrypc; - if (q->dw.function_entrypc (&entrypc)) - { - func.entrypc = entrypc; - q->filtered_functions.push_back (func); - } - else - /* this function just be fully inlined, just ignore it */ - return DWARF_CB_OK; - } - else if (q->has_statement_num) - { - func.entrypc = q->statement_num_val; - - // Translate literal address to symbol address, then - // compensate for dw bias (will be used for query dw funcs). - func.entrypc = q->dw.literal_addr_to_sym_addr(func.entrypc); - func.entrypc -= q->dw.module_bias; - - q->filtered_functions.push_back (func); - } - else - assert(0); - } + Dwarf_Addr entrypc; + if (q->dw.function_entrypc (&entrypc)) + { + func.entrypc = entrypc; + q->filtered_functions.push_back (func); + } + /* else this function is fully inlined, just ignore it */ } return DWARF_CB_OK; } @@ -1468,6 +1511,8 @@ static int query_cu (Dwarf_Die * cudie, void * arg) { dwarf_query * q = static_cast(arg); + assert (q->has_statement_str || q->has_function_str); + if (pending_interrupts) return DWARF_CB_ABORT; try @@ -1478,135 +1523,77 @@ query_cu (Dwarf_Die * cudie, void * arg) clog << "focused on CU '" << q->dw.cu_name() << "', in module '" << q->dw.module_name << "'\n"; - if (q->has_statement_str || q->has_statement_num - || q->has_function_str || q->has_function_num) - { - q->filtered_srcfiles.clear(); - q->filtered_functions.clear(); - q->filtered_inlines.clear(); - - // In this path, we find "abstract functions", record - // information about them, and then (depending on lineno - // matching) possibly emit one or more of the function's - // associated addresses. Unfortunately the control of this - // cannot easily be turned inside out. - - if ((q->has_statement_str || q->has_function_str) - && (q->spec_type != function_alone)) - { - // If we have a pattern string with a filename, we need - // to elaborate the srcfile mask in question first. - q->dw.collect_srcfiles_matching (q->file, q->filtered_srcfiles); - - // If we have a file pattern and *no* srcfile matches, there's - // no need to look further into this CU, so skip. - if (q->filtered_srcfiles.empty()) - return DWARF_CB_OK; - } - // Verify that a raw address matches the beginning of a - // statement. This is a somewhat lame check that the address - // is at the start of an assembly instruction. Mark probes are in the - // middle of a macro and thus not strictly at a statement beginning. - // Guru mode may override this check. - if (q->has_statement_num && ! q->has_mark) - { - Dwarf_Addr queryaddr = q->statement_num_val; - dwarf_line_t address_line(dwarf_getsrc_die(cudie, queryaddr)); - Dwarf_Addr lineaddr = 0; - if (address_line) - lineaddr = address_line.addr(); - if (!address_line || lineaddr != queryaddr) - { - stringstream msg; - msg << "address 0x" << hex << queryaddr - << " does not match the beginning of a statement"; - if (address_line) - msg << " (try 0x" << hex << lineaddr << ")"; - else - msg << " (no line info found for '" << q->dw.cu_name() - << "', in module '" << q->dw.module_name << "')"; - if (! q->sess.guru_mode) - throw semantic_error(msg.str()); - else if (! q->sess.suppress_warnings) - q->sess.print_warning(msg.str()); - } - } - // Pick up [entrypc, name, DIE] tuples for all the functions - // matching the query, and fill in the prologue endings of them - // all in a single pass. - int rc = q->dw.iterate_over_functions (query_dwarf_func, q, - q->function, - q->has_statement_num); - if (rc != DWARF_CB_OK) - q->query_done = true; - - if ((q->sess.prologue_searching || q->has_process) // PR 6871 - && !q->has_statement_str && !q->has_statement_num) // PR 2608 - if (! q->filtered_functions.empty()) - q->dw.resolve_prologue_endings (q->filtered_functions); - - if (q->has_label) - { - if (q->spec_type != function_file_and_line) // No line number specified - { - for (func_info_map_t::iterator i = q->filtered_functions.begin(); - i != q->filtered_functions.end(); ++i) - q->dw.iterate_over_labels (&i->die, q->label_val, i->name, - q, query_label); - - for (inline_instance_map_t::iterator i = q->filtered_inlines.begin(); - i != q->filtered_inlines.end(); ++i) - q->dw.iterate_over_labels (&i->die, q->label_val, i->name, - q, query_label); - } - else - for (set::const_iterator i = q->filtered_srcfiles.begin(); - i != q->filtered_srcfiles.end(); ++i) - q->dw.iterate_over_srcfile_lines (i->c_str(), q->line, q->has_statement_str, - q->line_type, query_srcfile_label, q->function, q); - } - else if ((q->has_statement_str || q->has_function_str) - && (q->spec_type == function_file_and_line)) - { - // If we have a pattern string with target *line*, we - // have to look at lines in all the matched srcfiles. - for (set::const_iterator i = q->filtered_srcfiles.begin(); - i != q->filtered_srcfiles.end(); ++i) - q->dw.iterate_over_srcfile_lines (i->c_str(), q->line, q->has_statement_str, - q->line_type, query_srcfile_line, q->function, q); - } - else - { - // Otherwise, simply probe all resolved functions. - for (func_info_map_t::iterator i = q->filtered_functions.begin(); - i != q->filtered_functions.end(); ++i) - query_func_info (i->entrypc, *i, q); - - // And all inline instances (if we're not excluding inlines with ".call") - if (! q->has_call) - for (inline_instance_map_t::iterator i - = q->filtered_inlines.begin(); i != q->filtered_inlines.end(); ++i) - query_inline_instance_info (*i, q); - } - } - else + q->filtered_srcfiles.clear(); + q->filtered_functions.clear(); + q->filtered_inlines.clear(); + + // In this path, we find "abstract functions", record + // information about them, and then (depending on lineno + // matching) possibly emit one or more of the function's + // associated addresses. Unfortunately the control of this + // cannot easily be turned inside out. + + if (q->spec_type != function_alone) { - // Before PR 5787, we used to have this: -#if 0 - // Otherwise we have a statement number, and we can just - // query it directly within this module. - assert (q->has_statement_num); - Dwarf_Addr query_addr = q->statement_num_val; - query_addr = q->dw.module_address_to_global(query_addr); - - query_statement ("", "", -1, NULL, query_addr, q); -#endif - // But now, we traverse CUs/functions even for - // statement_num's, for blacklist sensitivity and $var - // resolution purposes. + // If we have a pattern string with a filename, we need + // to elaborate the srcfile mask in question first. + q->dw.collect_srcfiles_matching (q->file, q->filtered_srcfiles); + + // If we have a file pattern and *no* srcfile matches, there's + // no need to look further into this CU, so skip. + if (q->filtered_srcfiles.empty()) + return DWARF_CB_OK; + } + + // Pick up [entrypc, name, DIE] tuples for all the functions + // matching the query, and fill in the prologue endings of them + // all in a single pass. + int rc = q->dw.iterate_over_functions (query_dwarf_func, q, + q->function, false); + if (rc != DWARF_CB_OK) + q->query_done = true; + + if ((q->sess.prologue_searching || q->has_process) // PR 6871 + && !q->has_statement_str) // PR 2608 + if (! q->filtered_functions.empty()) + q->dw.resolve_prologue_endings (q->filtered_functions); - assert (0); // NOTREACHED + if (q->spec_type == function_file_and_line) + { + // If we have a pattern string with target *line*, we + // have to look at lines in all the matched srcfiles. + void (* callback) (const dwarf_line_t&, void*) = + q->has_label ? query_srcfile_label : query_srcfile_line; + for (set::const_iterator i = q->filtered_srcfiles.begin(); + i != q->filtered_srcfiles.end(); ++i) + q->dw.iterate_over_srcfile_lines (i->c_str(), q->line, q->has_statement_str, + q->line_type, callback, q->function, q); + } + else if (q->has_label) + { + for (func_info_map_t::iterator i = q->filtered_functions.begin(); + i != q->filtered_functions.end(); ++i) + q->dw.iterate_over_labels (&i->die, q->label_val, i->name, + q, query_label); + + for (inline_instance_map_t::iterator i = q->filtered_inlines.begin(); + i != q->filtered_inlines.end(); ++i) + q->dw.iterate_over_labels (&i->die, q->label_val, i->name, + q, query_label); } + else + { + // Otherwise, simply probe all resolved functions. + for (func_info_map_t::iterator i = q->filtered_functions.begin(); + i != q->filtered_functions.end(); ++i) + query_func_info (i->entrypc, *i, q); + + // And all inline instances (if we're not excluding inlines with ".call") + if (! q->has_call) + for (inline_instance_map_t::iterator i + = q->filtered_inlines.begin(); i != q->filtered_inlines.end(); ++i) + query_inline_instance_info (*i, q); + } return DWARF_CB_OK; } catch (const semantic_error& e) -- cgit From 5aa987f2edc5c60b61d66fc0e5b7e0fb33423586 Mon Sep 17 00:00:00 2001 From: Mark Wielaard Date: Tue, 6 Oct 2009 15:22:21 +0200 Subject: PR10739 testcase. Split const_value test in two. Absolute const addr fails. * testsuite/systemtap.base/const_value.exp: Handle both const_value blocks and address separately. XFAIL second test as PR10739. * testsuite/systemtap.base/const_value.stp: Only query baz const value. * testsuite/systemtap.base/const_value_func.c: New test for bar address. * testsuite/systemtap.base/const_value_func.stp: Likewise. --- testsuite/systemtap.base/const_value.exp | 48 +++++++++++++++++++++++---- testsuite/systemtap.base/const_value.stp | 1 - testsuite/systemtap.base/const_value_func.c | 22 ++++++++++++ testsuite/systemtap.base/const_value_func.stp | 4 +++ 4 files changed, 68 insertions(+), 7 deletions(-) create mode 100644 testsuite/systemtap.base/const_value_func.c create mode 100644 testsuite/systemtap.base/const_value_func.stp diff --git a/testsuite/systemtap.base/const_value.exp b/testsuite/systemtap.base/const_value.exp index 5d5ceaee..afffebca 100644 --- a/testsuite/systemtap.base/const_value.exp +++ b/testsuite/systemtap.base/const_value.exp @@ -1,6 +1,6 @@ +# DW_AT_const_value (blocks). set test "const_value" -set ::result_string {f: bar -i: 2 +set ::result_string {i: 2 j: 21} set test_flags "additional_flags=-g" @@ -18,17 +18,53 @@ if { $res != "" } { pass "$test.c compile" } -# Test only when we are running an install test (can execute) and when gcc -# vta generated DW_AT_const_values for us. We are interested in both blocks -# and pure constant addresses (technically only the second was added by vta). +# Test only when we are running an install test (can execute) and when +# gcc generated DW_AT_const_values for us. We are interested in block +# constant values. if {[installtest_p] && [uprobes_p]} { set dw_at_c {DW_AT_const_value} if {![catch {exec readelf --debug-dump=info $test.exe | grep "$dw_at_c"}]} { stap_run2 $srcdir/$subdir/$test.stp -c ./$test.exe } { - untested "$test (no-gcc-vta)" + untested "$test (no-const-value)" } } else { untested "$test" } catch {exec rm -f $test.exe} + + +# DW_AT_const_value (address). +set test "const_value_func" +set ::result_string {f: bar} + +set test_flags "additional_flags=-g" +# We need -O2 to get const_value encodings in dwarf. +set test_flags "$test_flags additional_flags=-O2" +set test_flags "$test_flags additional_flags=-I$srcdir/../includes/sys" + +set res [target_compile $srcdir/$subdir/$test.c $test.exe executable "$test_flags"] +if { $res != "" } { + verbose "target_compile failed: $res" 2 + fail "$test.c compile" + untested "$test" + return +} else { + pass "$test.c compile" +} + +# Test only when we are running an install test (can execute) and when +# gcc generated DW_AT_const_values for us. We are interested in pure +# constant addresses. +if {[installtest_p] && [uprobes_p]} { + set dw_at_c {DW_AT_const_value} + if {![catch {exec readelf --debug-dump=info $test.exe | grep "$dw_at_c"}]} { + setup_xfail 10739 "*-*-*" + stap_run2 $srcdir/$subdir/$test.stp -c ./$test.exe + } { + untested "$test (no-const-value)" + } +} else { + untested "$test" +} +catch {exec rm -f $test.exe} \ No newline at end of file diff --git a/testsuite/systemtap.base/const_value.stp b/testsuite/systemtap.base/const_value.stp index 109d3d46..7aded0f2 100644 --- a/testsuite/systemtap.base/const_value.stp +++ b/testsuite/systemtap.base/const_value.stp @@ -1,6 +1,5 @@ probe process("const_value.exe").mark("constvalues") { - printf("f: %s\n", usymname($f)); printf("i: %d\n", $baz->i); printf("j: %d\n", $baz->j); } \ No newline at end of file diff --git a/testsuite/systemtap.base/const_value_func.c b/testsuite/systemtap.base/const_value_func.c new file mode 100644 index 00000000..23f2f0bc --- /dev/null +++ b/testsuite/systemtap.base/const_value_func.c @@ -0,0 +1,22 @@ +#include "sdt.h" + +static int +bar (int i, long j) +{ + return i * j; +} + +static int +func (int (*f) ()) +{ + volatile int i = 2; + volatile long j = 21; + STAP_PROBE (test, constvalues); + return f(i, j); +} + +int +main (int argc, char *argv[], char *envp[]) +{ + return func (&bar) - 42; +} diff --git a/testsuite/systemtap.base/const_value_func.stp b/testsuite/systemtap.base/const_value_func.stp new file mode 100644 index 00000000..9dad9150 --- /dev/null +++ b/testsuite/systemtap.base/const_value_func.stp @@ -0,0 +1,4 @@ +probe process("const_value_func.exe").mark("constvalues") +{ + printf("f: %s\n", usymname($f)); +} \ No newline at end of file -- cgit From e82cb6e94e5028637bdf0eedc0f6035139629a90 Mon Sep 17 00:00:00 2001 From: Mark Wielaard Date: Tue, 6 Oct 2009 15:26:58 +0200 Subject: PR10726 remove testcase workaround. * testsuite/systemtap.base/const_value.c: Allow inlining since PR10726 was fixed. --- testsuite/systemtap.base/const_value.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/testsuite/systemtap.base/const_value.c b/testsuite/systemtap.base/const_value.c index 4ff82161..2ed0f5c4 100644 --- a/testsuite/systemtap.base/const_value.c +++ b/testsuite/systemtap.base/const_value.c @@ -14,9 +14,7 @@ bar (const int i, const long j) return i * j; } -// Because of PR10726 we don't want to get this function inlined. -// We do need -O2 to get the const_value encodings in dwarf. -static __attribute__((__noinline__)) int +static int func (int (*f) ()) { const fooer baz = { .i = 2, .j = 21 }; -- cgit From 2155081e1d9888cf57334bc57abb3fff9b49d8e1 Mon Sep 17 00:00:00 2001 From: Charley Wang Date: Tue, 6 Oct 2009 10:26:36 -0400 Subject: PR10724: staprun: simplify permissions checking logic Pending advice from Frank and Dave, changed check_permission to return void and renamed it to assert_permission. assert_permission simply returns if permissions are okay, and calls exit(-1) if there are any permissions errors. --- runtime/staprun/staprun.c | 16 ++++++++-------- runtime/staprun/staprun_funcs.c | 22 +++++++++++----------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/runtime/staprun/staprun.c b/runtime/staprun/staprun.c index da3e304b..7b4aba1c 100644 --- a/runtime/staprun/staprun.c +++ b/runtime/staprun/staprun.c @@ -227,14 +227,14 @@ int init_staprun(void) without first removing the kernel module. This would block a subsequent rerun attempt. So here we gingerly try to unload it first. */ - int ret = delete_module (modname, O_NONBLOCK); - err("Retrying, after attempted removal of module %s (rc %d)\n", modname, ret); - /* Then we try an insert a second time. */ - if (insert_stap_module() < 0) - return -1; - } - if (send_relocations() < 0) - return -1; + int ret = delete_module (modname, O_NONBLOCK); + err("Retrying, after attempted removal of module %s (rc %d)\n", modname, ret); + /* Then we try an insert a second time. */ + if (insert_stap_module() < 0) + return -1; + } + if (send_relocations() < 0) + return -1; } return 0; } diff --git a/runtime/staprun/staprun_funcs.c b/runtime/staprun/staprun_funcs.c index 6ef96111..e4ccc8da 100644 --- a/runtime/staprun/staprun_funcs.c +++ b/runtime/staprun/staprun_funcs.c @@ -23,7 +23,7 @@ #include extern long init_module(void *, unsigned long, const char *); -static int check_permissions(const void *, off_t); +static void assert_permissions(const void *, off_t); /* Module errors get translated. */ const char *moderror(int err) @@ -112,10 +112,10 @@ int insert_module(const char *path, const char *special_options, char **options) return -1; } - /* Check whether this module can be loaded by the current user. */ - ret = check_permissions (file, sbuf.st_size); - if (ret != 1) - return -1; + /* Check whether this module can be loaded by the current user. + * check_permissions will exit(-1) if permissions are insufficient*/ + assert_permissions (file, sbuf.st_size); + STAP_PROBE1(staprun, insert__module, path); /* Actually insert the module */ @@ -448,7 +448,7 @@ check_groups (void) * * Returns: -1 on errors, 0 on failure, 1 on success. */ -int check_permissions( +void assert_permissions( const void *module_data __attribute__ ((unused)), off_t module_size __attribute__ ((unused)) ) { @@ -460,7 +460,7 @@ int check_permissions( if the module has been tampered with (altered). */ check_signature_rc = check_signature (module_data, module_size); if (check_signature_rc == MODULE_ALTERED) - return 0; + exit(-1); #endif /* If we're root, we can do anything. */ @@ -477,20 +477,20 @@ int check_permissions( err("WARNING: couldn't set staprun GID to '%s': %s", env_id, strerror(errno)); - return 1; + return; } /* Check permissions for group membership. */ check_groups_rc = check_groups (); if (check_groups_rc == 1) - return 1; + return; /* The user is an ordinary user. If the module has been signed with * an authorized certificate and private key, then we will load it for * anyone. */ #if HAVE_NSS if (check_signature_rc == MODULE_OK) - return 1; + return; assert (check_signature_rc == MODULE_UNTRUSTED || check_signature_rc == MODULE_CHECK_ERROR); #endif @@ -509,5 +509,5 @@ int check_permissions( #endif /* Combine the return codes. They are either 0 or -1. */ - return check_groups_rc | check_signature_rc; + exit(-1); } -- cgit From 47f025139d1c2e75781cdab40dc9195396133754 Mon Sep 17 00:00:00 2001 From: Mark Wielaard Date: Tue, 6 Oct 2009 19:24:22 +0200 Subject: Add proc_mem tapset, functions to query memory usage of the current process. * tapset/proc_mem.stp: New tapset. * testsuite/buildok/proc_mem.stp * doc/SystemTap_Tapset_Reference/tapsets.tmpl (memory_stp): Include tapset/proc_mem.stp. --- doc/SystemTap_Tapset_Reference/tapsets.tmpl | 4 +- tapset/proc_mem.stp | 194 ++++++++++++++++++++++++++++ testsuite/buildok/proc_mem.stp | 13 ++ 3 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 tapset/proc_mem.stp create mode 100755 testsuite/buildok/proc_mem.stp diff --git a/doc/SystemTap_Tapset_Reference/tapsets.tmpl b/doc/SystemTap_Tapset_Reference/tapsets.tmpl index 99f72727..705cd3b5 100644 --- a/doc/SystemTap_Tapset_Reference/tapsets.tmpl +++ b/doc/SystemTap_Tapset_Reference/tapsets.tmpl @@ -138,10 +138,12 @@ Memory Tapset - This family of probe points is used to probe memory-related events. + This family of probe points is used to probe memory-related events + or query the memory usage of the current process. It contains the following probe points: !Itapset/memory.stp +!Itapset/proc_mem.stp diff --git a/tapset/proc_mem.stp b/tapset/proc_mem.stp new file mode 100644 index 00000000..1493bd92 --- /dev/null +++ b/tapset/proc_mem.stp @@ -0,0 +1,194 @@ +// Process memory query and utility functions. +// Copyright (C) 2009 Red Hat Inc. +// +// This file is part of systemtap, and is free software. You can +// redistribute it and/or modify it under the terms of the GNU General +// Public License (GPL); either version 2, or (at your option) any +// later version. + +// +// Process memory query and utility functions provide information about +// the memory usage of the current application. These functions provide +// information about the full size, resident, shared, code and data used +// by the current process. And provide utility functions to query the +// page size of the current architecture and create human readable string +// representations of bytes and pages used. +// + +%{ +/* PF_BORROWED_MM got renamed to PF_KTHREAD with same semantics somewhere. */ +#ifdef PF_BORROWED_MM +#define _STP_PF_KTHREAD PF_BORROWED_MM +#else +#define _STP_PF_KTHREAD PF_KTHREAD +#endif + /* Returns the mm for the current proc. Slightly paranoid. Only returns + if the task isn't starting, exiting or (coopted by) a kernel thread. */ + static struct mm_struct *_stp_proc_mm(void) + { + if (current->flags & (_STP_PF_KTHREAD | PF_EXITING | PF_STARTING)) + return NULL; + return current->mm; + } +%} + +/** + * sfunction proc_mem_size - Total program virtual memory size in pages. + * + * Description: Returns the total virtual memory size in pages of the + * current process, or zero when there is no current process or the + * number of pages couldn't be retrieved. + */ +function proc_mem_size:long () +%{ /* pure */ /* unprivileged */ + struct mm_struct *mm = _stp_proc_mm (); + if (mm) + THIS->__retvalue = mm->total_vm; + else + THIS->__retvalue = 0; +%} + +/** + * sfunction proc_mem_rss - Program resident set size in pages. + * + * Description: Returns the resident set size in pages of the current + * process, or zero when there is no current process or the number of + * pages couldn't be retrieved. + */ +function proc_mem_rss:long () +%{ /* pure */ /* unprivileged */ + struct mm_struct *mm = _stp_proc_mm (); + if (mm) + THIS->__retvalue = (get_mm_counter(mm, file_rss) + + get_mm_counter(mm, anon_rss)); + else + THIS->__retvalue = 0; +%} + +/** + * sfunction proc_mem_shr - Program shared pages (from shared mappings). + * + * Description: Returns the shared pages (from shared mappings) of the + * current process, or zero when there is no current process or the + * number of pages couldn't be retrieved. + */ +function proc_mem_shr:long () +%{ /* pure */ /* unprivileged */ + struct mm_struct *mm = _stp_proc_mm (); + if (mm) + THIS->__retvalue = get_mm_counter(mm, file_rss); + else + THIS->__retvalue = 0; +%} + +/** + * sfunction proc_mem_txt - Program text (code) size in pages. + * + * Description: Returns the current process text (code) size in pages, + * or zero when there is no current process or the number of pages + * couldn't be retrieved. + */ +function proc_mem_txt:long () +%{ /* pure */ /* unprivileged */ + struct mm_struct *mm = _stp_proc_mm (); + if (mm) + THIS->__retvalue = (PAGE_ALIGN(mm->end_code) + - (mm->start_code & PAGE_MASK)) >> PAGE_SHIFT; + else + THIS->__retvalue = 0; +%} + +/** + * sfunction proc_mem_data - Program data size (data + stack) in pages. + * + * Description: Returns the current process data size (data + stack) + * in pages, or zero when there is no current process or the number of + * pages couldn't be retrieved. + */ +function proc_mem_data:long () +%{ /* pure */ /* unprivileged */ + struct mm_struct *mm = _stp_proc_mm (); + if (mm) + THIS->__retvalue = mm->total_vm - mm->shared_vm; + else + THIS->__retvalue = 0; +%} + +/** + * sfunction mem_page_size - Number of bytes in a page for this architecture. + */ +function mem_page_size:long () +%{ /* pure */ /* unprivileged */ + THIS->__retvalue = PAGE_SIZE; +%} + +// Return a 5 character wide string " x.yyp", " xx.yp", " xxxp", "xxxxp". +function _stp_number_to_string_postfix:string (x:long, y:long, p:string) +{ + if (x < 10) + return sprintf("%d.%.2d%s", x, y * 100 / 1024, p); + if (x < 100) + return sprintf("%2d.%d%s", x, y * 10 / 1024, p); + return sprintf("%4d%s", x, p); +} + +/** + * sfunction bytes_to_string - Human readable string for given bytes. + * + * Description: Returns a string representing the number of bytes (up + * to 1024 bytes), the number of kilobytes (when less than 1024K) + * postfixed by 'K', the number of megabytes (when less than 1024M) + * postfixed by 'M' or the number of gigabytes postfixed by 'G'. If + * representing K, M or G, and the number is amount is less than 100, + * it includes a '.' plus the remainer. The returned string will be 5 + * characters wide (padding with whitespace at the front) unless + * negative or representing more than 9999G bytes. + */ +function bytes_to_string:string (bytes:long) +{ + if (bytes < 1024) + return sprintf("%5d", bytes); + + remain = bytes % 1024; + bytes = bytes / 1024; + if (bytes < 1024) + return _stp_number_to_string_postfix(bytes, remain, "K"); + + remain = bytes % 1024; + bytes = bytes / 1024; + if (bytes < 1024) + return _stp_number_to_string_postfix(bytes, remain, "M"); + + remain = bytes % 1024; + bytes = bytes / 1024; + return _stp_number_to_string_postfix(bytes, remain, "G"); +} + +/** + * sfunction pages_to_string - Turns pages into a human readable string. + * + * Description: Multiplies pages by page_size() to get the number of + * bytes and returns the result of bytes_to_string(). + */ +function pages_to_string:string (pages:long) +{ + bytes = pages * mem_page_size(); + return bytes_to_string (bytes); +} + +/** + * sfunction proc_mem_string - Human readable string of current proc memory usage. + * + * Description: Returns a human readable string showing the size, rss, + * shr, txt and data of the memory used by the current process. + * For example "size: 301m, rss: 11m, shr: 8m, txt: 52k, data: 2248k". + */ +function proc_mem_string:string () +{ + return sprintf ("size: %s, rss: %s, shr: %s, txt: %s, data: %s", + pages_to_string(proc_mem_size()), + pages_to_string(proc_mem_rss()), + pages_to_string(proc_mem_shr()), + pages_to_string(proc_mem_txt()), + pages_to_string(proc_mem_data())); +} diff --git a/testsuite/buildok/proc_mem.stp b/testsuite/buildok/proc_mem.stp new file mode 100755 index 00000000..8fc2512a --- /dev/null +++ b/testsuite/buildok/proc_mem.stp @@ -0,0 +1,13 @@ +#! stap -p4 + +probe begin { + printf("%d\n", proc_mem_size()); + printf("%d\n", proc_mem_rss()); + printf("%d\n", proc_mem_shr()); + printf("%d\n", proc_mem_txt()); + printf("%d\n", proc_mem_data()); + printf("%d\n", mem_page_size()) + printf("%s\n", bytes_to_string(0)); + printf("%s\n", pages_to_string(0)); + printf("%s\n", proc_mem_string()); +} -- cgit From 038c38c6119e29189be83c3a214c635c0d02ee58 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 6 Oct 2009 20:01:13 -0700 Subject: Remove the global derived_probe->semaphore map Instead just make the semaphore address a member of derived_probe. * session.h (systemtap_session): Remove the map sdt_semaphore_addr. * elaborate.h (derived_probe): Add sdt_semaphore_addr directly. * tapsets.cxx (sdt_query::record_semaphore): Write the addr directly. (uprobe_derived_probe_group::emit_module_decls): Read it directly. * tapset-utrace.cxx (utrace_derived_probe_group::emit_probe_decl): Ditto --- elaborate.cxx | 4 ++-- elaborate.h | 7 +++++++ session.h | 4 ---- tapset-utrace.cxx | 7 ++----- tapsets.cxx | 7 +++---- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/elaborate.cxx b/elaborate.cxx index 01c6e3cd..2d75058d 100644 --- a/elaborate.cxx +++ b/elaborate.cxx @@ -55,7 +55,7 @@ expression* add_condition (expression* a, expression* b) derived_probe::derived_probe (probe *p): - base (p) + base (p), sdt_semaphore_addr(0) { assert (p); this->locations = p->locations; @@ -66,7 +66,7 @@ derived_probe::derived_probe (probe *p): derived_probe::derived_probe (probe *p, probe_point *l): - base (p) + base (p), sdt_semaphore_addr(0) { assert (p); this->tok = p->tok; diff --git a/elaborate.h b/elaborate.h index cd60b8bb..28294aa9 100644 --- a/elaborate.h +++ b/elaborate.h @@ -18,6 +18,10 @@ #include #include +extern "C" { +#include +} + // ------------------------------------------------------------------------ struct derived_probe; @@ -153,6 +157,9 @@ public: virtual bool needs_global_locks () { return true; } // by default, probes need locks around global variables + + // Location of semaphores to activate sdt probes + Dwarf_Addr sdt_semaphore_addr; }; // ------------------------------------------------------------------------ diff --git a/session.h b/session.h index 760b610a..a2176793 100644 --- a/session.h +++ b/session.h @@ -221,10 +221,6 @@ struct systemtap_session void print_error_source (std::ostream&, std::string&, const token* tok); void print_warning (const std::string& w, const token* tok = 0); - - // Location of semaphores to activate sdt probes - std::map sdt_semaphore_addr; - // NB: It is very important for all of the above (and below) fields // to be cleared in the systemtap_session ctor (elaborate.cxx) // and/or main.cxx(main). diff --git a/tapset-utrace.cxx b/tapset-utrace.cxx index b13dc290..d2ce9dd7 100644 --- a/tapset-utrace.cxx +++ b/tapset-utrace.cxx @@ -717,12 +717,9 @@ utrace_derived_probe_group::emit_probe_decl (systemtap_session& s, } s.op->line() << " .engine_attached=0,"; - map::iterator its = s.sdt_semaphore_addr.find(p); - if (its == s.sdt_semaphore_addr.end()) - s.op->line() << " .sdt_sem_address=(unsigned long)0x0,"; - else + if (p->sdt_semaphore_addr != 0) s.op->line() << " .sdt_sem_address=(unsigned long)0x" - << hex << its->second << dec << "ULL,"; + << hex << p->sdt_semaphore_addr << dec << "ULL,"; s.op->line() << " .tsk=0,"; s.op->line() << " },"; diff --git a/tapsets.cxx b/tapsets.cxx index 6267f314..09ead991 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -3699,7 +3699,7 @@ sdt_query::record_semaphore (vector & results, unsigned start) if (dwfl_module_relocations (dw.module) > 0) dwfl_module_relocate_address (dw.module, &addr); for (unsigned i = start; i < results.size(); ++i) - sess.sdt_semaphore_addr.insert(make_pair(results[i], addr)); + results[i]->sdt_semaphore_addr = addr; } } @@ -4447,10 +4447,9 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->line() << " .pp=" << lex_cast_qstring (*p->sole_location()) << ","; s.op->line() << " .ph=&" << p->name << ","; - map::iterator its = s.sdt_semaphore_addr.find(p); - if (its != s.sdt_semaphore_addr.end()) + if (p->sdt_semaphore_addr != 0) s.op->line() << " .sdt_sem_address=(unsigned long)0x" - << hex << its->second << dec << "ULL,"; + << hex << p->sdt_semaphore_addr << dec << "ULL,"; if (p->has_return) s.op->line() << " .return_p=1,"; -- cgit