diff options
author | Tim Moore <timoore@redhat.com> | 2009-10-07 20:10:17 +0200 |
---|---|---|
committer | Tim Moore <timoore@redhat.com> | 2009-10-07 20:10:17 +0200 |
commit | e6638df404688a1af5e9713befc298984241af5b (patch) | |
tree | a3684cfc7825e9079a021a38732e5d4ce4260c1c | |
parent | 9ed28fbc61420dbcfe46828fe5ee35eae3f4dc79 (diff) | |
parent | 038c38c6119e29189be83c3a214c635c0d02ee58 (diff) | |
download | systemtap-steved-e6638df404688a1af5e9713befc298984241af5b.tar.gz systemtap-steved-e6638df404688a1af5e9713befc298984241af5b.tar.xz systemtap-steved-e6638df404688a1af5e9713befc298984241af5b.zip |
Merge commit 'origin/master'
32 files changed, 1224 insertions, 334 deletions
@@ -27,6 +27,7 @@ Kai Meyer Keiichi KII Kent Sebastian Kevin Stafford +Kiran Prakesh Li Guanglei Lubomir Rintel Mahesh J Salgaonkar @@ -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 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 @@ <chapter id="memory_stp"> <title>Memory Tapset</title> <para> - 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: </para> !Itapset/memory.stp +!Itapset/proc_mem.stp </chapter> <chapter id="iosched.stp"> @@ -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<Dwarf_Die>& 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 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 <sstream> #include <map> +extern "C" { +#include <elfutils/libdw.h> +} + // ------------------------------------------------------------------------ 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; }; // ------------------------------------------------------------------------ @@ -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; @@ -1526,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; @@ -1608,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); @@ -1617,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; @@ -1626,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) { @@ -1637,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) @@ -1746,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) { @@ -1763,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; 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 <assert.h> 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); } @@ -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<derived_probe*, Dwarf_Addr> 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/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 <algorithm> +#include <iostream> +#include <fstream> +#include <sstream> #include <set> #include <string> @@ -77,6 +81,107 @@ static set<string> 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/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 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 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<derived_probe*, Dwarf_Addr>::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/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; i<x; i++) r .= " " - - return r + return sprintf("%6d %s:%-*s", (ts - _indent_timestamps[idx]), desc, (x>0 ? x-1 : 0), "") } /** 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. + +// <tapsetdescription> +// 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. +// </tapsetdescription> + +%{ +/* 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/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/tapsets.cxx b/tapsets.cxx index 4433af3f..09ead991 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 { @@ -1146,6 +1137,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<Dwarf_Die> 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, char const * file, @@ -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<dwarf_query *>(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<dwarf_query *>(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<dwarf_query *>(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<string>::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<string>::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<string>::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) @@ -3712,7 +3699,7 @@ sdt_query::record_semaphore (vector<derived_probe *> & 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; } } @@ -4460,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<derived_probe*, Dwarf_Addr>::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,"; 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 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()); +} 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.base/const_value.c b/testsuite/systemtap.base/const_value.c new file mode 100644 index 00000000..2ed0f5c4 --- /dev/null +++ b/testsuite/systemtap.base/const_value.c @@ -0,0 +1,29 @@ +#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; +} + +static 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..afffebca --- /dev/null +++ b/testsuite/systemtap.base/const_value.exp @@ -0,0 +1,70 @@ +# DW_AT_const_value (blocks). +set test "const_value" +set ::result_string {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 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-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 new file mode 100644 index 00000000..7aded0f2 --- /dev/null +++ b/testsuite/systemtap.base/const_value.stp @@ -0,0 +1,5 @@ +probe process("const_value.exe").mark("constvalues") +{ + 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 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: <a href="keyword-index.html#SYSCALL">SYSCALL</a> <a href="keyword-inde <li><a href="profiling/functioncallcount.stp">profiling/functioncallcount.stp</a> - Count Times Functions Called<br> keywords: <a href="keyword-index.html#PROFILING">PROFILING</a> <a href="keyword-index.html#FUNCTIONS">FUNCTIONS</a> <br> <p>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.</p></li> +<li><a href="profiling/sched_switch.stp">profiling/sched_switch.stp</a> - Display the task switches happening in the scheduler<br> +keywords: <a href="keyword-index.html#PROFILING">PROFILING</a> <a href="keyword-index.html#FUNCTIONS">FUNCTIONS</a> <br> +<p>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.</p></li> <li><a href="profiling/thread-times.stp">profiling/thread-times.stp</a> - Profile kernel functions<br> keywords: <a href="keyword-index.html#PROFILING">PROFILING</a> <br> <p>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.</p></li> 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: <a href="keyword-index.html#NETWORK">NETWORK</a> <a href="keyword-inde <li><a href="profiling/functioncallcount.stp">profiling/functioncallcount.stp</a> - Count Times Functions Called<br> keywords: <a href="keyword-index.html#PROFILING">PROFILING</a> <a href="keyword-index.html#FUNCTIONS">FUNCTIONS</a> <br> <p>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.</p></li> +<li><a href="profiling/sched_switch.stp">profiling/sched_switch.stp</a> - Display the task switches happening in the scheduler<br> +keywords: <a href="keyword-index.html#PROFILING">PROFILING</a> <a href="keyword-index.html#FUNCTIONS">FUNCTIONS</a> <br> +<p>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.</p></li> </ul> <h3><a name="FUTEX">FUTEX</a></h3> <ul> @@ -300,6 +303,9 @@ keywords: <a href="keyword-index.html#PROFILING">PROFILING</a> <br> <li><a href="profiling/functioncallcount.stp">profiling/functioncallcount.stp</a> - Count Times Functions Called<br> keywords: <a href="keyword-index.html#PROFILING">PROFILING</a> <a href="keyword-index.html#FUNCTIONS">FUNCTIONS</a> <br> <p>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.</p></li> +<li><a href="profiling/sched_switch.stp">profiling/sched_switch.stp</a> - Display the task switches happening in the scheduler<br> +keywords: <a href="keyword-index.html#PROFILING">PROFILING</a> <a href="keyword-index.html#FUNCTIONS">FUNCTIONS</a> <br> +<p>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.</p></li> <li><a href="profiling/thread-times.stp">profiling/thread-times.stp</a> - Profile kernel functions<br> keywords: <a href="keyword-index.html#PROFILING">PROFILING</a> <br> <p>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.</p></li> 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 new file mode 100644 index 00000000..b202a74c --- /dev/null +++ b/testsuite/systemtap.examples/profiling/sched_switch.meta @@ -0,0 +1,14 @@ +title: Display the task switches happening in the scheduler +name: sched_switch.stp +version: 1.0 +author: kiran +keywords: profiling functions +subsystem: kernel +status: production +exit: user-controlled +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 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 new file mode 100755 index 00000000..1c1b18b7 --- /dev/null +++ b/testsuite/systemtap.examples/profiling/sched_switch.stp @@ -0,0 +1,63 @@ +#! /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 ->/+ + * 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) +} |