summaryrefslogtreecommitdiffstats
path: root/dwflpp.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'dwflpp.cxx')
-rw-r--r--dwflpp.cxx2159
1 files changed, 2159 insertions, 0 deletions
diff --git a/dwflpp.cxx b/dwflpp.cxx
new file mode 100644
index 00000000..08083291
--- /dev/null
+++ b/dwflpp.cxx
@@ -0,0 +1,2159 @@
+// C++ interface to dwfl
+// Copyright (C) 2005-2009 Red Hat Inc.
+// Copyright (C) 2005-2007 Intel Corporation.
+// Copyright (C) 2008 James.Bottomley@HansenPartnership.com
+//
+// 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.
+
+#include "dwflpp.h"
+#include "config.h"
+#include "staptree.h"
+#include "elaborate.h"
+#include "tapsets.h"
+#include "task_finder.h"
+#include "translate.h"
+#include "session.h"
+#include "util.h"
+#include "buildrun.h"
+#include "dwarf_wrappers.h"
+#include "auto_free.h"
+#include "hash.h"
+
+#include <cstdlib>
+#include <algorithm>
+#include <deque>
+#include <iostream>
+#include <map>
+#ifdef HAVE_TR1_UNORDERED_MAP
+#include <tr1/unordered_map>
+#else
+#include <ext/hash_map>
+#endif
+#include <set>
+#include <sstream>
+#include <stdexcept>
+#include <vector>
+#include <cstdarg>
+#include <cassert>
+#include <iomanip>
+#include <cerrno>
+
+extern "C" {
+#include <fcntl.h>
+#include <elfutils/libdwfl.h>
+#include <elfutils/libdw.h>
+#include <dwarf.h>
+#include <elf.h>
+#include <obstack.h>
+#include <regex.h>
+#include <glob.h>
+#include <fnmatch.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "loc2c.h"
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+}
+
+
+using namespace std;
+using namespace __gnu_cxx;
+
+
+static string TOK_KERNEL("kernel");
+
+
+dwflpp::dwflpp(systemtap_session & session, const string& user_module):
+ sess(session), dwfl(NULL), module(NULL), module_dwarf(NULL),
+ module_bias(0), mod_info(NULL), module_start(0), module_end(0),
+ cu(NULL), function(NULL)
+{
+ if (user_module.empty())
+ setup_kernel();
+ else
+ setup_user(user_module);
+}
+
+
+dwflpp::~dwflpp()
+{
+ if (dwfl)
+ dwfl_end(dwfl);
+}
+
+
+string const
+dwflpp::default_name(char const * in, char const *)
+{
+ if (in)
+ return in;
+ return string("");
+}
+
+
+void
+dwflpp::get_module_dwarf(bool required, bool report)
+{
+ module_dwarf = dwfl_module_getdwarf(module, &module_bias);
+ mod_info->dwarf_status = (module_dwarf ? info_present : info_absent);
+ if (!module_dwarf && report)
+ {
+ string msg = "cannot find ";
+ if (module_name == "")
+ msg += "kernel";
+ else
+ msg += string("module ") + module_name;
+ msg += " debuginfo";
+
+ int i = dwfl_errno();
+ if (i)
+ msg += string(": ") + dwfl_errmsg (i);
+
+ if (required)
+ throw semantic_error (msg);
+ else
+ cerr << "WARNING: " << msg << "\n";
+ }
+}
+
+
+void
+dwflpp::focus_on_module(Dwfl_Module * m, module_info * mi)
+{
+ module = m;
+ mod_info = mi;
+ if (m)
+ {
+ module_name = default_name(dwfl_module_info(module, NULL,
+ &module_start, &module_end,
+ NULL, NULL, NULL, NULL),
+ "module");
+ }
+ else
+ {
+ assert(mi && mi->name && mi->name == TOK_KERNEL);
+ module_name = mi->name;
+ module_start = 0;
+ module_end = 0;
+ module_bias = mi->bias;
+ }
+
+ // Reset existing pointers and names
+
+ module_dwarf = NULL;
+
+ cu_name.clear();
+ cu = NULL;
+
+ function_name.clear();
+ function = NULL;
+}
+
+
+void
+dwflpp::focus_on_cu(Dwarf_Die * c)
+{
+ assert(c);
+ assert(module);
+
+ cu = c;
+ cu_name = default_name(dwarf_diename(c), "CU");
+
+ // Reset existing pointers and names
+ function_name.clear();
+ function = NULL;
+}
+
+
+void
+dwflpp::focus_on_function(Dwarf_Die * f)
+{
+ assert(f);
+ assert(module);
+ assert(cu);
+
+ function = f;
+ function_name = default_name(dwarf_diename(function), "function");
+}
+
+
+void
+dwflpp::focus_on_module_containing_global_address(Dwarf_Addr a)
+{
+ assert(dwfl);
+ cu = NULL;
+ Dwfl_Module* mod = dwfl_addrmodule(dwfl, a);
+ if (mod) // address could be wildly out of range
+ focus_on_module(mod, NULL);
+}
+
+
+void
+dwflpp::query_cu_containing_global_address(Dwarf_Addr a, void *arg)
+{
+ Dwarf_Addr bias;
+ assert(dwfl);
+ get_module_dwarf();
+ Dwarf_Die* cudie = dwfl_module_addrdie(module, a, &bias);
+ if (cudie) // address could be wildly out of range
+ query_cu (cudie, arg);
+ assert(bias == module_bias);
+}
+
+
+void
+dwflpp::query_cu_containing_module_address(Dwarf_Addr a, void *arg)
+{
+ query_cu_containing_global_address(module_address_to_global(a), arg);
+}
+
+
+Dwarf_Addr
+dwflpp::module_address_to_global(Dwarf_Addr a)
+{
+ assert(dwfl);
+ assert(module);
+ get_module_dwarf();
+ if (module_name == TOK_KERNEL || dwfl_module_relocations (module) <= 0)
+ return a;
+ return a + module_start;
+}
+
+
+Dwarf_Addr
+dwflpp::global_address_to_module(Dwarf_Addr a)
+{
+ assert(module);
+ get_module_dwarf();
+ return a - module_bias;
+}
+
+
+bool
+dwflpp::module_name_matches(string pattern)
+{
+ bool t = (fnmatch(pattern.c_str(), module_name.c_str(), 0) == 0);
+ if (t && sess.verbose>3)
+ clog << "pattern '" << pattern << "' "
+ << "matches "
+ << "module '" << module_name << "'" << "\n";
+ return t;
+}
+
+
+bool
+dwflpp::name_has_wildcard(string pattern)
+{
+ return (pattern.find('*') != string::npos ||
+ pattern.find('?') != string::npos ||
+ pattern.find('[') != string::npos);
+}
+
+
+bool
+dwflpp::module_name_final_match(string pattern)
+{
+ // Assume module_name_matches(). Can there be any more matches?
+ // Not unless the pattern is a wildcard, since module names are
+ // presumed unique.
+ return !name_has_wildcard(pattern);
+}
+
+
+bool
+dwflpp::function_name_matches_pattern(string name, string pattern)
+{
+ bool t = (fnmatch(pattern.c_str(), name.c_str(), 0) == 0);
+ if (t && sess.verbose>3)
+ clog << "pattern '" << pattern << "' "
+ << "matches "
+ << "function '" << name << "'" << "\n";
+ return t;
+}
+
+
+bool
+dwflpp::function_name_matches(string pattern)
+{
+ assert(function);
+ return function_name_matches_pattern(function_name, pattern);
+}
+
+
+bool
+dwflpp::function_name_final_match(string pattern)
+{
+ return module_name_final_match (pattern);
+}
+
+
+bool
+dwflpp::cu_name_matches(string pattern)
+{
+ assert(cu);
+
+ // PR 5049: implicit * in front of given path pattern.
+ // NB: fnmatch() is used without FNM_PATHNAME.
+ string prefixed_pattern = string("*/") + pattern;
+
+ bool t = (fnmatch(pattern.c_str(), cu_name.c_str(), 0) == 0 ||
+ fnmatch(prefixed_pattern.c_str(), cu_name.c_str(), 0) == 0);
+ if (t && sess.verbose>3)
+ clog << "pattern '" << prefixed_pattern << "' "
+ << "matches "
+ << "CU '" << cu_name << "'" << "\n";
+ return t;
+}
+
+
+void
+dwflpp::setup_kernel(bool debuginfo_needed)
+{
+ // XXX: See also translate.cxx:emit_symbol_data
+
+ if (! sess.module_cache)
+ sess.module_cache = new module_cache ();
+
+ static const char *debuginfo_path_arr = "+:.debug:/usr/lib/debug:build";
+ static const char *debuginfo_env_arr = getenv("SYSTEMTAP_DEBUGINFO_PATH");
+ static const char *debuginfo_path = (debuginfo_env_arr ?: debuginfo_path_arr );
+
+ static const Dwfl_Callbacks kernel_callbacks =
+ {
+ dwfl_linux_kernel_find_elf,
+ dwfl_standard_find_debuginfo,
+ dwfl_offline_section_address,
+ (char **) & debuginfo_path
+ };
+
+ dwfl = dwfl_begin (&kernel_callbacks);
+ if (!dwfl)
+ throw semantic_error ("cannot open dwfl");
+ dwfl_report_begin (dwfl);
+
+ // We have a problem with -r REVISION vs -r BUILDDIR here. If
+ // we're running against a fedora/rhel style kernel-debuginfo
+ // tree, s.kernel_build_tree is not the place where the unstripped
+ // vmlinux will be installed. Rather, it's over yonder at
+ // /usr/lib/debug/lib/modules/$REVISION/. It seems that there is
+ // 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 (sess.kernel_build_tree == string("/lib/modules/" + sess.kernel_release + "/build"))
+ elfutils_kernel_path = sess.kernel_release;
+ else
+ elfutils_kernel_path = sess.kernel_build_tree;
+
+ int rc = dwfl_linux_kernel_report_offline (dwfl,
+ elfutils_kernel_path.c_str(),
+ &dwfl_report_offline_predicate);
+
+ if (debuginfo_needed)
+ dwfl_assert (string("missing ") + sess.architecture +
+ string(" kernel/module debuginfo under '") +
+ sess.kernel_build_tree + string("'"),
+ rc);
+
+ // XXX: it would be nice if we could do a single
+ // ..._report_offline call for an entire systemtap script, so
+ // that a selection predicate would filter out modules outside
+ // the union of all the requested wildcards. But we build
+ // derived_probes one-by-one and we don't have lookahead.
+ // PR 3498.
+
+ // XXX: a special case: if we have only kernel.* probe points,
+ // we shouldn't waste time looking for module debug-info (and
+ // vice versa).
+
+ // NB: the result of an _offline call is the assignment of
+ // virtualized addresses to relocatable objects such as
+ // modules. These have to be converted to real addresses at
+ // run time. See the dwarf_derived_probe ctor and its caller.
+
+ dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl, NULL, NULL));
+}
+
+
+void
+dwflpp::setup_user(const string& module_name, bool debuginfo_needed)
+{
+ if (! sess.module_cache)
+ sess.module_cache = new module_cache ();
+
+ static const char *debuginfo_path_arr = "+:.debug:/usr/lib/debug:build";
+ static const char *debuginfo_env_arr = getenv("SYSTEMTAP_DEBUGINFO_PATH");
+ // NB: kernel_build_tree doesn't enter into this, as it's for
+ // kernel-side modules only.
+ static const char *debuginfo_path = (debuginfo_env_arr ?: debuginfo_path_arr);
+
+ static const Dwfl_Callbacks user_callbacks =
+ {
+ NULL, /* dwfl_linux_kernel_find_elf, */
+ dwfl_standard_find_debuginfo,
+ dwfl_offline_section_address,
+ (char **) & debuginfo_path
+ };
+
+ dwfl = dwfl_begin (&user_callbacks);
+ if (!dwfl)
+ throw semantic_error ("cannot open dwfl");
+ dwfl_report_begin (dwfl);
+
+ // XXX: should support buildid-based naming
+
+ Dwfl_Module *mod = dwfl_report_offline (dwfl,
+ module_name.c_str(),
+ module_name.c_str(),
+ -1);
+ // XXX: save mod!
+
+ if (debuginfo_needed)
+ dwfl_assert (string("missing process ") +
+ module_name +
+ string(" ") +
+ sess.architecture +
+ string(" debuginfo"),
+ mod);
+
+ if (!module)
+ module = mod;
+
+ // NB: the result of an _offline call is the assignment of
+ // virtualized addresses to relocatable objects such as
+ // modules. These have to be converted to real addresses at
+ // run time. See the dwarf_derived_probe ctor and its caller.
+
+ dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl, NULL, NULL));
+}
+
+
+void
+dwflpp::iterate_over_modules(int (* callback)(Dwfl_Module *, void **,
+ const char *, Dwarf_Addr,
+ void *),
+ base_query *data)
+{
+ ptrdiff_t off = 0;
+ do
+ {
+ if (pending_interrupts) return;
+ off = dwfl_getmodules (dwfl, callback, data, off);
+ }
+ while (off > 0);
+ // Don't complain if we exited dwfl_getmodules early.
+ // This could be a $target variable error that will be
+ // reported soon anyway.
+ // dwfl_assert("dwfl_getmodules", off == 0);
+
+ // PR6864 XXX: For dwarfless case (if .../vmlinux is missing), then the
+ // "kernel" module is not reported in the loop above. However, we
+ // may be able to make do with symbol table data.
+}
+
+
+void
+dwflpp::query_modules(base_query *q)
+{
+ iterate_over_modules(&query_module, q);
+}
+
+
+void
+dwflpp::iterate_over_cus (int (*callback)(Dwarf_Die * die, void * arg),
+ void * data)
+{
+ get_module_dwarf(false);
+ Dwarf *dw = module_dwarf;
+ if (!dw) return;
+
+ vector<Dwarf_Die>* v = module_cu_cache[dw];
+ if (v == 0)
+ {
+ v = new vector<Dwarf_Die>;
+ module_cu_cache[dw] = v;
+
+ Dwarf_Off off = 0;
+ size_t cuhl;
+ Dwarf_Off noff;
+ while (dwarf_nextcu (dw, off, &noff, &cuhl, NULL, NULL, NULL) == 0)
+ {
+ if (pending_interrupts) return;
+ Dwarf_Die die_mem;
+ Dwarf_Die *die;
+ die = dwarf_offdie (dw, off + cuhl, &die_mem);
+ v->push_back (*die); /* copy */
+ off = noff;
+ }
+ }
+
+ for (unsigned i = 0; i < v->size(); i++)
+ {
+ if (pending_interrupts) return;
+ Dwarf_Die die = v->at(i);
+ int rc = (*callback)(& die, data);
+ if (rc != DWARF_CB_OK) break;
+ }
+}
+
+
+bool
+dwflpp::func_is_inline()
+{
+ assert (function);
+ return dwarf_func_inline (function) != 0;
+}
+
+
+int
+dwflpp::cu_inl_function_caching_callback (Dwarf_Die* func, void *arg)
+{
+ vector<Dwarf_Die>* v = static_cast<vector<Dwarf_Die>*>(arg);
+ v->push_back (* func);
+ return DWARF_CB_OK;
+}
+
+
+void
+dwflpp::iterate_over_inline_instances (int (* callback)(Dwarf_Die * die, void * arg),
+ void * data)
+{
+ assert (function);
+ assert (func_is_inline ());
+
+ string key = module_name + ":" + cu_name + ":" + function_name;
+ vector<Dwarf_Die>* v = cu_inl_function_cache[key];
+ if (v == 0)
+ {
+ v = new vector<Dwarf_Die>;
+ cu_inl_function_cache[key] = v;
+ dwarf_func_inline_instances (function, cu_inl_function_caching_callback, v);
+ }
+
+ for (unsigned i=0; i<v->size(); i++)
+ {
+ if (pending_interrupts) return;
+ Dwarf_Die die = v->at(i);
+ int rc = (*callback)(& die, data);
+ if (rc != DWARF_CB_OK) break;
+ }
+}
+
+
+int
+dwflpp::global_alias_caching_callback(Dwarf_Die *die, void *arg)
+{
+ cu_function_cache_t *cache = static_cast<cu_function_cache_t*>(arg);
+ const char *name = dwarf_diename(die);
+
+ if (!name)
+ return DWARF_CB_OK;
+
+ string structure_name = name;
+
+ if (!dwarf_hasattr(die, DW_AT_declaration) &&
+ cache->find(structure_name) == cache->end())
+ (*cache)[structure_name] = *die;
+
+ return DWARF_CB_OK;
+}
+
+
+Dwarf_Die *
+dwflpp::declaration_resolve(const char *name)
+{
+ if (!name)
+ return NULL;
+
+ string key = module_name + ":" + cu_name;
+ cu_function_cache_t *v = global_alias_cache[key];
+ if (v == 0) // need to build the cache, just once per encountered module/cu
+ {
+ v = new cu_function_cache_t;
+ global_alias_cache[key] = v;
+ iterate_over_globals(global_alias_caching_callback, v);
+ if (sess.verbose > 4)
+ clog << "global alias cache " << key << " size " << v->size() << endl;
+ }
+
+ // XXX: it may be desirable to search other modules' declarations
+ // too, in case a module/shared-library processes a
+ // forward-declared pointer type only, where the actual definition
+ // may only be in vmlinux or the application.
+
+ // XXX: it is probably desirable to search other CU's declarations
+ // in the same module.
+
+ if (v->find(name) == v->end())
+ return NULL;
+
+ return & ((*v)[name]);
+}
+
+
+int
+dwflpp::cu_function_caching_callback (Dwarf_Die* func, void *arg)
+{
+ cu_function_cache_t* v = static_cast<cu_function_cache_t*>(arg);
+ const char *name = dwarf_diename(func);
+ if (!name)
+ return DWARF_CB_OK;
+
+ string function_name = name;
+ (*v)[function_name] = * func;
+ return DWARF_CB_OK;
+}
+
+
+int
+dwflpp::iterate_over_functions (int (* callback)(Dwarf_Die * func, base_query * q),
+ base_query * q, const string& function,
+ bool has_statement_num)
+{
+ int rc = DWARF_CB_OK;
+ assert (module);
+ assert (cu);
+
+ string key = module_name + ":" + cu_name;
+ cu_function_cache_t *v = cu_function_cache[key];
+ if (v == 0)
+ {
+ v = new cu_function_cache_t;
+ cu_function_cache[key] = v;
+ dwarf_getfuncs (cu, cu_function_caching_callback, v, 0);
+ if (sess.verbose > 4)
+ clog << "function cache " << key << " size " << v->size() << endl;
+ }
+
+ string subkey = function;
+ if (v->find(subkey) != v->end())
+ {
+ Dwarf_Die die = v->find(subkey)->second;
+ if (sess.verbose > 4)
+ clog << "function cache " << key << " hit " << subkey << endl;
+ return (*callback)(& die, q);
+ }
+ else if (name_has_wildcard (subkey))
+ {
+ for (cu_function_cache_t::iterator it = v->begin(); it != v->end(); it++)
+ {
+ if (pending_interrupts) return DWARF_CB_ABORT;
+ string func_name = it->first;
+ Dwarf_Die die = it->second;
+ if (function_name_matches_pattern (func_name, subkey))
+ {
+ if (sess.verbose > 4)
+ clog << "function cache " << key << " match " << func_name << " vs "
+ << subkey << endl;
+
+ rc = (*callback)(& die, q);
+ if (rc != DWARF_CB_OK) break;
+ }
+ }
+ }
+ else if (has_statement_num) // searching all for kernel.statement
+ {
+ for (cu_function_cache_t::iterator it = v->begin(); it != v->end(); it++)
+ {
+ Dwarf_Die die = it->second;
+ rc = (*callback)(& die, q);
+ if (rc != DWARF_CB_OK) break;
+ }
+ }
+ else // not a wildcard and no match in this CU
+ {
+ // do nothing
+ }
+ return rc;
+}
+
+
+/* This basically only goes one level down from the compile unit so it
+ * only picks up top level stuff (i.e. nothing in a lower scope) */
+int
+dwflpp::iterate_over_globals (int (* callback)(Dwarf_Die *, void *),
+ void * data)
+{
+ int rc = DWARF_CB_OK;
+ Dwarf_Die die;
+
+ assert (module);
+ assert (cu);
+ assert (dwarf_tag(cu) == DW_TAG_compile_unit);
+
+ if (dwarf_child(cu, &die) != 0)
+ return rc;
+
+ do
+ /* We're only currently looking for named types,
+ * although other types of declarations exist */
+ switch (dwarf_tag(&die))
+ {
+ case DW_TAG_base_type:
+ case DW_TAG_enumeration_type:
+ case DW_TAG_structure_type:
+ case DW_TAG_typedef:
+ case DW_TAG_union_type:
+ rc = (*callback)(&die, data);
+ break;
+ }
+ while (rc == DWARF_CB_OK && dwarf_siblingof(&die, &die) == 0);
+
+ return rc;
+}
+
+
+// This little test routine represents an unfortunate breakdown in
+// abstraction between dwflpp (putatively, a layer right on top of
+// elfutils), and dwarf_query (interpreting a systemtap probe point).
+// It arises because we sometimes try to fix up slightly-off
+// .statement() probes (something we find out in fairly low-level).
+//
+// An alternative would be to put some more intelligence into query_cu(),
+// and have it print additional suggestions after finding that
+// q->dw.iterate_over_srcfile_lines resulted in no new finished_results.
+
+bool
+dwflpp::has_single_line_record (dwarf_query * q, char const * srcfile, int lineno)
+{
+ if (lineno < 0)
+ return false;
+
+ Dwarf_Line **srcsp = NULL;
+ size_t nsrcs = 0;
+
+ dwarf_assert ("dwarf_getsrc_file",
+ dwarf_getsrc_file (module_dwarf,
+ srcfile, lineno, 0,
+ &srcsp, &nsrcs));
+
+ if (nsrcs != 1)
+ {
+ if (sess.verbose>4)
+ clog << "alternative line " << lineno << " rejected: nsrcs=" << nsrcs << endl;
+ return false;
+ }
+
+ // We also try to filter out lines that leave the selected
+ // functions (if any).
+
+ dwarf_line_t line(srcsp[0]);
+ Dwarf_Addr addr = line.addr();
+
+ func_info_map_t *filtered_functions = get_filtered_functions(q);
+ for (func_info_map_t::iterator i = filtered_functions->begin();
+ i != filtered_functions->end(); ++i)
+ {
+ if (die_has_pc (i->die, addr))
+ {
+ if (sess.verbose>4)
+ clog << "alternative line " << lineno << " accepted: fn=" << i->name << endl;
+ return true;
+ }
+ }
+
+ inline_instance_map_t *filtered_inlines = get_filtered_inlines(q);
+ for (inline_instance_map_t::iterator i = filtered_inlines->begin();
+ i != filtered_inlines->end(); ++i)
+ {
+ if (die_has_pc (i->die, addr))
+ {
+ if (sess.verbose>4)
+ clog << "alternative line " << lineno << " accepted: ifn=" << i->name << endl;
+ return true;
+ }
+ }
+
+ if (sess.verbose>4)
+ clog << "alternative line " << lineno << " rejected: leaves selected fns" << endl;
+ return false;
+}
+
+
+void
+dwflpp::iterate_over_srcfile_lines (char const * srcfile,
+ int lines[2],
+ bool need_single_match,
+ enum line_t line_type,
+ void (* callback) (const dwarf_line_t& line,
+ void * arg),
+ void *data)
+{
+ Dwarf_Line **srcsp = NULL;
+ size_t nsrcs = 0;
+ dwarf_query * q = static_cast<dwarf_query *>(data);
+ int lineno = lines[0];
+ auto_free_ref<Dwarf_Line**> free_srcsp(srcsp);
+
+ get_module_dwarf();
+
+ if (line_type == RELATIVE)
+ {
+ Dwarf_Addr addr;
+ Dwarf_Line *line;
+ int line_number;
+
+ dwarf_assert ("dwarf_entrypc", dwarf_entrypc (this->function, &addr));
+ line = dwarf_getsrc_die (this->cu, addr);
+ dwarf_assert ("dwarf_getsrc_die", line == NULL);
+ dwarf_assert ("dwarf_lineno", dwarf_lineno (line, &line_number));
+ lineno += line_number;
+ }
+ else if (line_type == WILDCARD)
+ function_line (&lineno);
+
+ for (int l = lineno; ; l = l + 1)
+ {
+ set<int> lines_probed;
+ pair<set<int>::iterator,bool> line_probed;
+ dwarf_assert ("dwarf_getsrc_file",
+ dwarf_getsrc_file (module_dwarf,
+ srcfile, l, 0,
+ &srcsp, &nsrcs));
+ if (line_type == WILDCARD || line_type == RANGE)
+ {
+ Dwarf_Addr line_addr;
+ dwarf_lineno (srcsp [0], &lineno);
+ line_probed = lines_probed.insert(lineno);
+ if (lineno != l || line_probed.second == false || nsrcs > 1)
+ continue;
+ dwarf_lineaddr (srcsp [0], &line_addr);
+ if (dwarf_haspc (function, line_addr) != 1)
+ break;
+ }
+
+ // NB: Formerly, we used to filter, because:
+
+ // dwarf_getsrc_file gets one *near hits* for line numbers, not
+ // exact matches. For example, an existing file but a nonexistent
+ // line number will be rounded up to the next definition in that
+ // file. This may be similar to the GDB breakpoint algorithm, but
+ // we don't want to be so fuzzy in systemtap land. So we filter.
+
+ // But we now see the error of our ways, and skip this filtering.
+
+ // XXX: the code also fails to match e.g. inline function
+ // definitions when the srcfile is a header file rather than the
+ // CU name.
+
+ size_t remaining_nsrcs = nsrcs;
+
+ if (need_single_match && remaining_nsrcs > 1)
+ {
+ // We wanted a single line record (a unique address for the
+ // line) and we got a bunch of line records. We're going to
+ // skip this probe (throw an exception) but before we throw
+ // we're going to look around a bit to see if there's a low or
+ // high line number nearby which *doesn't* have this problem,
+ // so we can give the user some advice.
+
+ int lo_try = -1;
+ int hi_try = -1;
+ for (size_t i = 1; i < 6; ++i)
+ {
+ if (lo_try == -1 && has_single_line_record(q, srcfile, lineno - i))
+ lo_try = lineno - i;
+
+ if (hi_try == -1 && has_single_line_record(q, srcfile, lineno + i))
+ hi_try = lineno + i;
+ }
+
+ stringstream advice;
+ advice << "multiple addresses for " << srcfile << ":" << lineno;
+ if (lo_try > 0 || hi_try > 0)
+ {
+ advice << " (try ";
+ if (lo_try > 0)
+ advice << srcfile << ":" << lo_try;
+ if (lo_try > 0 && hi_try > 0)
+ advice << " or ";
+ if (hi_try > 0)
+ advice << srcfile << ":" << hi_try;
+ advice << ")";
+ }
+ throw semantic_error (advice.str());
+ }
+
+ for (size_t i = 0; i < nsrcs; ++i)
+ {
+ if (pending_interrupts) return;
+ if (srcsp [i]) // skip over mismatched lines
+ callback (dwarf_line_t(srcsp[i]), data);
+ }
+
+ if (line_type == ABSOLUTE || line_type == RELATIVE)
+ break;
+ else if (line_type == RANGE && l == lines[1])
+ break;
+ }
+}
+
+
+void
+dwflpp::iterate_over_labels (Dwarf_Die *begin_die,
+ const char *sym,
+ const char *symfunction,
+ void *data,
+ void (* callback)(const string &,
+ const char *,
+ int,
+ Dwarf_Die *,
+ Dwarf_Addr,
+ dwarf_query *))
+{
+ dwarf_query * q __attribute__ ((unused)) = static_cast<dwarf_query *>(data) ;
+
+ get_module_dwarf();
+
+ Dwarf_Die die;
+ int res = dwarf_child (begin_die, &die);
+ if (res != 0)
+ return; // die without children, bail out.
+
+ static string function_name = dwarf_diename (begin_die);
+ do
+ {
+ Dwarf_Attribute attr_mem;
+ Dwarf_Attribute *attr = dwarf_attr (&die, DW_AT_name, &attr_mem);
+ int tag = dwarf_tag(&die);
+ const char *name = dwarf_formstring (attr);
+ if (name == 0)
+ continue;
+ switch (tag)
+ {
+ case DW_TAG_label:
+ break;
+ case DW_TAG_subprogram:
+ if (!dwarf_hasattr(&die, DW_AT_declaration))
+ function_name = name;
+ else
+ continue;
+ default:
+ if (dwarf_haschildren (&die))
+ iterate_over_labels (&die, sym, symfunction, q, callback);
+ continue;
+ }
+
+ if (strcmp(function_name.c_str(), symfunction) == 0
+ || (name_has_wildcard(symfunction)
+ && function_name_matches (symfunction)))
+ {
+ }
+ else
+ continue;
+ if (strcmp(name, sym) == 0
+ || (name_has_wildcard(sym)
+ && function_name_matches_pattern (name, sym)))
+ {
+ const char *file = dwarf_decl_file (&die);
+ // Get the line number for this label
+ Dwarf_Attribute attr;
+ dwarf_attr (&die,DW_AT_decl_line, &attr);
+ Dwarf_Sword dline;
+ dwarf_formsdata (&attr, &dline);
+ Dwarf_Addr stmt_addr;
+ if (dwarf_lowpc (&die, &stmt_addr) != 0)
+ {
+ // There is no lowpc so figure out the address
+ // Get the real die for this cu
+ Dwarf_Die cudie;
+ dwarf_diecu (cu, &cudie, NULL, NULL);
+ size_t nlines = 0;
+ // Get the line for this label
+ Dwarf_Line **aline;
+ dwarf_getsrc_file (module_dwarf, file, (int)dline, 0, &aline, &nlines);
+ // Get the address
+ for (size_t i = 0; i < nlines; i++)
+ {
+ dwarf_lineaddr (*aline, &stmt_addr);
+ if ((dwarf_haspc (&die, stmt_addr)))
+ break;
+ }
+ }
+
+ Dwarf_Die *scopes;
+ int nscopes = 0;
+ nscopes = dwarf_getscopes_die (&die, &scopes);
+ if (nscopes > 1)
+ {
+ callback(function_name.c_str(), file,
+ (int)dline, &scopes[1], stmt_addr, q);
+ add_label_name(q, name);
+ }
+ }
+ }
+ while (dwarf_siblingof (&die, &die) == 0);
+}
+
+
+void
+dwflpp::collect_srcfiles_matching (string const & pattern,
+ set<char const *> & filtered_srcfiles)
+{
+ assert (module);
+ assert (cu);
+
+ size_t nfiles;
+ Dwarf_Files *srcfiles;
+
+ // PR 5049: implicit * in front of given path pattern.
+ // NB: fnmatch() is used without FNM_PATHNAME.
+ string prefixed_pattern = string("*/") + pattern;
+
+ dwarf_assert ("dwarf_getsrcfiles",
+ dwarf_getsrcfiles (cu, &srcfiles, &nfiles));
+ {
+ for (size_t i = 0; i < nfiles; ++i)
+ {
+ char const * fname = dwarf_filesrc (srcfiles, i, NULL, NULL);
+ if (fnmatch (pattern.c_str(), fname, 0) == 0 ||
+ fnmatch (prefixed_pattern.c_str(), fname, 0) == 0)
+ {
+ filtered_srcfiles.insert (fname);
+ if (sess.verbose>2)
+ clog << "selected source file '" << fname << "'\n";
+ }
+ }
+ }
+}
+
+
+void
+dwflpp::resolve_prologue_endings (func_info_map_t & funcs)
+{
+ // This heuristic attempts to pick the first address that has a
+ // source line distinct from the function declaration's. In a
+ // perfect world, this would be the first statement *past* the
+ // prologue.
+
+ assert(module);
+ assert(cu);
+
+ size_t nlines = 0;
+ Dwarf_Lines *lines = NULL;
+
+ /* trouble cases:
+ malloc do_symlink in init/initramfs.c tail-recursive/tiny then no-prologue
+ sys_get?id in kernel/timer.c no-prologue
+ sys_exit_group tail-recursive
+ {do_,}sys_open extra-long-prologue (gcc 3.4)
+ cpu_to_logical_apicid NULL-decl_file
+ */
+
+ // Fetch all srcline records, sorted by address.
+ dwarf_assert ("dwarf_getsrclines",
+ dwarf_getsrclines(cu, &lines, &nlines));
+ // XXX: free lines[] later, but how?
+
+ for(func_info_map_t::iterator it = funcs.begin(); it != funcs.end(); it++)
+ {
+#if 0 /* someday */
+ Dwarf_Addr* bkpts = 0;
+ int n = dwarf_entry_breakpoints (& it->die, & bkpts);
+ // ...
+ free (bkpts);
+#endif
+
+ Dwarf_Addr entrypc = it->entrypc;
+ Dwarf_Addr highpc; // NB: highpc is exclusive: [entrypc,highpc)
+ dwfl_assert ("dwarf_highpc", dwarf_highpc (& it->die,
+ & highpc));
+
+ if (it->decl_file == 0) it->decl_file = "";
+
+ unsigned entrypc_srcline_idx = 0;
+ dwarf_line_t entrypc_srcline;
+ // open-code binary search for exact match
+ {
+ unsigned l = 0, h = nlines;
+ while (l < h)
+ {
+ entrypc_srcline_idx = (l + h) / 2;
+ const dwarf_line_t lr(dwarf_onesrcline(lines,
+ entrypc_srcline_idx));
+ Dwarf_Addr addr = lr.addr();
+ if (addr == entrypc) { entrypc_srcline = lr; break; }
+ else if (l + 1 == h) { break; } // ran off bottom of tree
+ else if (addr < entrypc) { l = entrypc_srcline_idx; }
+ else { h = entrypc_srcline_idx; }
+ }
+ }
+ if (!entrypc_srcline)
+ {
+ if (sess.verbose > 2)
+ clog << "missing entrypc dwarf line record for function '"
+ << it->name << "'\n";
+ // This is probably an inlined function. We'll end up using
+ // its lowpc as a probe address.
+ continue;
+ }
+
+ if (sess.verbose>2)
+ clog << "prologue searching function '" << it->name << "'"
+ << " 0x" << hex << entrypc << "-0x" << highpc << dec
+ << "@" << it->decl_file << ":" << it->decl_line
+ << "\n";
+
+ // Now we go searching for the first line record that has a
+ // file/line different from the one in the declaration.
+ // Normally, this will be the next one. BUT:
+ //
+ // We may have to skip a few because some old compilers plop
+ // in dummy line records for longer prologues. If we go too
+ // far (addr >= highpc), we take the previous one. Or, it may
+ // be the first one, if the function had no prologue, and thus
+ // the entrypc maps to a statement in the body rather than the
+ // declaration.
+
+ unsigned postprologue_srcline_idx = entrypc_srcline_idx;
+ bool ranoff_end = false;
+ while (postprologue_srcline_idx < nlines)
+ {
+ dwarf_line_t lr(dwarf_onesrcline(lines, postprologue_srcline_idx));
+ Dwarf_Addr postprologue_addr = lr.addr();
+ const char* postprologue_file = lr.linesrc();
+ int postprologue_lineno = lr.lineno();
+
+ if (sess.verbose>2)
+ clog << "checking line record 0x" << hex << postprologue_addr << dec
+ << "@" << postprologue_file << ":" << postprologue_lineno << "\n";
+
+ if (postprologue_addr >= highpc)
+ {
+ ranoff_end = true;
+ postprologue_srcline_idx --;
+ continue;
+ }
+ if (ranoff_end ||
+ (strcmp (postprologue_file, it->decl_file) || // We have a winner!
+ (postprologue_lineno != it->decl_line)))
+ {
+ it->prologue_end = postprologue_addr;
+
+ if (sess.verbose>2)
+ {
+ clog << "prologue found function '" << it->name << "'";
+ // Add a little classification datum
+ if (postprologue_srcline_idx == entrypc_srcline_idx) clog << " (naked)";
+ if (ranoff_end) clog << " (tail-call?)";
+ clog << " = 0x" << hex << postprologue_addr << dec << "\n";
+ }
+
+ break;
+ }
+
+ // Let's try the next srcline.
+ postprologue_srcline_idx ++;
+ } // loop over srclines
+
+ // if (strlen(it->decl_file) == 0) it->decl_file = NULL;
+
+ } // loop over functions
+
+ // XXX: how to free lines?
+}
+
+
+bool
+dwflpp::function_entrypc (Dwarf_Addr * addr)
+{
+ assert (function);
+ return (dwarf_entrypc (function, addr) == 0);
+ // XXX: see also _lowpc ?
+}
+
+
+bool
+dwflpp::die_entrypc (Dwarf_Die * die, Dwarf_Addr * addr)
+{
+ int rc = 0;
+ string lookup_method;
+
+ * addr = 0;
+
+ lookup_method = "dwarf_entrypc";
+ rc = dwarf_entrypc (die, addr);
+
+ if (rc)
+ {
+ lookup_method = "dwarf_lowpc";
+ rc = dwarf_lowpc (die, addr);
+ }
+
+ if (rc)
+ {
+ lookup_method = "dwarf_ranges";
+
+ Dwarf_Addr base;
+ Dwarf_Addr begin;
+ Dwarf_Addr end;
+ ptrdiff_t offset = dwarf_ranges (die, 0, &base, &begin, &end);
+ if (offset < 0) rc = -1;
+ else if (offset > 0)
+ {
+ * addr = begin;
+ rc = 0;
+
+ // Now we need to check that there are no more ranges
+ // associated with this function, which could conceivably
+ // happen if a function is inlined, then pieces of it are
+ // split amongst different conditional branches. It's not
+ // obvious which of them to favour. As a heuristic, we
+ // pick the beginning of the first range, and ignore the
+ // others (but with a warning).
+
+ unsigned extra = 0;
+ while ((offset = dwarf_ranges (die, offset, &base, &begin, &end)) > 0)
+ extra ++;
+ if (extra)
+ lookup_method += ", ignored " + lex_cast<string>(extra) + " more";
+ }
+ }
+
+ if (sess.verbose > 2)
+ clog << "entry-pc lookup (" << lookup_method << ") = 0x" << hex << *addr << dec
+ << " (rc " << rc << ")"
+ << endl;
+ return (rc == 0);
+}
+
+
+void
+dwflpp::function_die (Dwarf_Die *d)
+{
+ assert (function);
+ *d = *function;
+}
+
+
+void
+dwflpp::function_file (char const ** c)
+{
+ assert (function);
+ assert (c);
+ *c = dwarf_decl_file (function);
+}
+
+
+void
+dwflpp::function_line (int *linep)
+{
+ assert (function);
+ dwarf_decl_line (function, linep);
+}
+
+
+bool
+dwflpp::die_has_pc (Dwarf_Die & die, Dwarf_Addr pc)
+{
+ int res = dwarf_haspc (&die, pc);
+ // dwarf_ranges will return -1 if a function die has no DW_AT_ranges
+ // if (res == -1)
+ // dwarf_assert ("dwarf_haspc", res);
+ return res == 1;
+}
+
+
+void
+dwflpp::loc2c_error (void *, const char *fmt, ...)
+{
+ const char *msg = "?";
+ char *tmp = NULL;
+ int rc;
+ va_list ap;
+ va_start (ap, fmt);
+ rc = vasprintf (& tmp, fmt, ap);
+ if (rc < 0)
+ msg = "?";
+ else
+ msg = tmp;
+ va_end (ap);
+ throw semantic_error (msg);
+}
+
+
+// This function generates code used for addressing computations of
+// target variables.
+void
+dwflpp::emit_address (struct obstack *pool, Dwarf_Addr address)
+{
+ #if 0
+ // The easy but incorrect way is to just print a hard-wired
+ // constant.
+ obstack_printf (pool, "%#" PRIx64 "UL", address);
+ #endif
+
+ // Turn this address into a section-relative offset if it should be one.
+ // We emit a comment approximating the variable+offset expression that
+ // relocatable module probing code will need to have.
+ Dwfl_Module *mod = dwfl_addrmodule (dwfl, address);
+ dwfl_assert ("dwfl_addrmodule", mod);
+ const char *modname = dwfl_module_info (mod, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL);
+ int n = dwfl_module_relocations (mod);
+ dwfl_assert ("dwfl_module_relocations", n >= 0);
+ Dwarf_Addr reloc_address = address;
+ int i = dwfl_module_relocate_address (mod, &reloc_address);
+ dwfl_assert ("dwfl_module_relocate_address", i >= 0);
+ dwfl_assert ("dwfl_module_info", modname);
+ const char *secname = dwfl_module_relocation_info (mod, i, NULL);
+
+ if (sess.verbose > 2)
+ {
+ clog << "emit dwarf addr 0x" << hex << address << dec
+ << " => module " << modname
+ << " section " << (secname ?: "null")
+ << " relocaddr 0x" << hex << reloc_address << dec
+ << endl;
+ }
+
+ if (n > 0 && !(n == 1 && secname == NULL))
+ {
+ dwfl_assert ("dwfl_module_relocation_info", secname);
+ if (n > 1 || secname[0] != '\0')
+ {
+ // This gives us the module name, and section name within the
+ // module, for a kernel module (or other ET_REL module object).
+ obstack_printf (pool, "({ static unsigned long addr = 0; ");
+ obstack_printf (pool, "if (addr==0) addr = _stp_module_relocate (\"%s\",\"%s\",%#" PRIx64 "); ",
+ modname, secname, reloc_address);
+ obstack_printf (pool, "addr; })");
+ }
+ else if (n == 1 && module_name == TOK_KERNEL && secname[0] == '\0')
+ {
+ // elfutils' way of telling us that this is a relocatable kernel address, which we
+ // need to treat the same way here as dwarf_query::add_probe_point does: _stext.
+ address -= sess.sym_stext;
+ secname = "_stext";
+ obstack_printf (pool, "({ static unsigned long addr = 0; ");
+ obstack_printf (pool, "if (addr==0) addr = _stp_module_relocate (\"%s\",\"%s\",%#" PRIx64 "); ",
+ modname, secname, address); // PR10000 NB: not reloc_address
+ obstack_printf (pool, "addr; })");
+ }
+ else
+ {
+ throw semantic_error ("cannot relocate user-space dso (?) address");
+#if 0
+ // This would happen for a Dwfl_Module that's a user-level DSO.
+ obstack_printf (pool, " /* %s+%#" PRIx64 " */",
+ modname, address);
+#endif
+ }
+ }
+ else
+ obstack_printf (pool, "%#" PRIx64 "UL", address); // assume as constant
+}
+
+
+void
+dwflpp::loc2c_emit_address (void *arg, struct obstack *pool,
+ Dwarf_Addr address)
+{
+ dwflpp *dwfl = (dwflpp *) arg;
+ dwfl->emit_address (pool, address);
+}
+
+
+void
+dwflpp::print_locals(Dwarf_Die *die, ostream &o)
+{
+ // Try to get the first child of die.
+ Dwarf_Die child;
+ if (dwarf_child (die, &child) == 0)
+ {
+ do
+ {
+ const char *name;
+ // Output each sibling's name (that is a variable or
+ // parameter) to 'o'.
+ switch (dwarf_tag (&child))
+ {
+ case DW_TAG_variable:
+ case DW_TAG_formal_parameter:
+ name = dwarf_diename (&child);
+ if (name)
+ o << " " << name;
+ break;
+ default:
+ break;
+ }
+ }
+ while (dwarf_siblingof (&child, &child) == 0);
+ }
+}
+
+
+Dwarf_Attribute *
+dwflpp::find_variable_and_frame_base (Dwarf_Die *scope_die,
+ Dwarf_Addr pc,
+ string const & local,
+ const target_symbol *e,
+ Dwarf_Die *vardie,
+ Dwarf_Attribute *fb_attr_mem)
+{
+ Dwarf_Die *scopes;
+ int nscopes = 0;
+ Dwarf_Attribute *fb_attr = NULL;
+
+ assert (cu);
+
+ nscopes = dwarf_getscopes (cu, pc, &scopes);
+ int sidx;
+ // if pc and scope_die are disjoint then we need dwarf_getscopes_die
+ for (sidx = 0; sidx < nscopes; sidx++)
+ if (scopes[sidx].addr == scope_die->addr)
+ break;
+ if (sidx == nscopes)
+ nscopes = dwarf_getscopes_die (scope_die, &scopes);
+
+ if (nscopes <= 0)
+ {
+ throw semantic_error ("unable to find any scopes containing "
+ + lex_cast_hex<string>(pc)
+ + ((scope_die == NULL) ? ""
+ : (string (" in ")
+ + (dwarf_diename(scope_die) ?: "<unknown>")
+ + "(" + (dwarf_diename(cu) ?: "<unknown>")
+ + ")"))
+ + " while searching for local '" + local + "'",
+ e->tok);
+ }
+
+ int declaring_scope = dwarf_getscopevar (scopes, nscopes,
+ local.c_str(),
+ 0, NULL, 0, 0,
+ vardie);
+ if (declaring_scope < 0)
+ {
+ stringstream alternatives;
+ print_locals (scopes, alternatives);
+ throw semantic_error ("unable to find local '" + local + "'"
+ + " near pc " + lex_cast_hex<string>(pc)
+ + ((scope_die == NULL) ? ""
+ : (string (" in ")
+ + (dwarf_diename(scope_die) ?: "<unknown>")
+ + "(" + (dwarf_diename(cu) ?: "<unknown>")
+ + ")"))
+ + (alternatives.str() == "" ? "" : (" (alternatives:" + alternatives.str () + ")")),
+ e->tok);
+ }
+
+ for (int inner = 0; inner < nscopes; ++inner)
+ {
+ switch (dwarf_tag (&scopes[inner]))
+ {
+ default:
+ continue;
+ case DW_TAG_subprogram:
+ case DW_TAG_entry_point:
+ case DW_TAG_inlined_subroutine: /* XXX */
+ if (inner >= declaring_scope)
+ fb_attr = dwarf_attr_integrate (&scopes[inner],
+ DW_AT_frame_base,
+ fb_attr_mem);
+ break;
+ }
+ }
+ return fb_attr;
+}
+
+
+struct location *
+dwflpp::translate_location(struct obstack *pool,
+ Dwarf_Attribute *attr, Dwarf_Addr pc,
+ Dwarf_Attribute *fb_attr,
+ struct location **tail,
+ const target_symbol *e)
+{
+ Dwarf_Op *expr;
+ size_t len;
+
+ /* PR9768: formerly, we added pc+module_bias here. However, that bias value
+ is not present in the pc value by the time we get it, so adding it would
+ result in false negatives of variable reachibility. In other instances
+ further below, the c_translate_FOO functions, the module_bias value used
+ to be passed in, but instead should now be zero for the same reason. */
+
+ switch (dwarf_getlocation_addr (attr, pc /*+ module_bias*/, &expr, &len, 1))
+ {
+ case 1: /* Should always happen. */
+ if (len > 0)
+ break;
+ /* Fall through. */
+
+ case 0: /* Shouldn't happen. */
+ throw semantic_error ("not accessible at this address", e->tok);
+
+ default: /* Shouldn't happen. */
+ case -1:
+ throw semantic_error (string ("dwarf_getlocation_addr failed") +
+ string (dwarf_errmsg (-1)),
+ e->tok);
+ }
+
+ return c_translate_location (pool, &loc2c_error, this,
+ &loc2c_emit_address,
+ 1, 0 /* PR9768 */,
+ pc, expr, len, tail, fb_attr);
+}
+
+
+void
+dwflpp::print_members(Dwarf_Die *vardie, ostream &o)
+{
+ const int typetag = dwarf_tag (vardie);
+
+ if (typetag != DW_TAG_structure_type && typetag != DW_TAG_union_type)
+ {
+ o << " Error: "
+ << (dwarf_diename_integrate (vardie) ?: "<anonymous>")
+ << " isn't a struct/union";
+ return;
+ }
+
+ // Try to get the first child of vardie.
+ Dwarf_Die die_mem;
+ Dwarf_Die *die = &die_mem;
+ switch (dwarf_child (vardie, die))
+ {
+ case 1: // No children.
+ o << ((typetag == DW_TAG_union_type) ? " union " : " struct ")
+ << (dwarf_diename_integrate (die) ?: "<anonymous>")
+ << " is empty";
+ break;
+
+ case -1: // Error.
+ default: // Shouldn't happen.
+ o << ((typetag == DW_TAG_union_type) ? " union " : " struct ")
+ << (dwarf_diename_integrate (die) ?: "<anonymous>")
+ << ": " << dwarf_errmsg (-1);
+ break;
+
+ case 0: // Success.
+ break;
+ }
+
+ // Output each sibling's name to 'o'.
+ while (dwarf_tag (die) == DW_TAG_member)
+ {
+ const char *member = dwarf_diename_integrate (die) ;
+
+ if ( member != NULL )
+ o << " " << member;
+ else
+ {
+ Dwarf_Die temp_die = *die;
+ Dwarf_Attribute temp_attr ;
+
+ if (!dwarf_attr_integrate (&temp_die, DW_AT_type, &temp_attr))
+ {
+ clog << "\n Error in obtaining type attribute for "
+ << (dwarf_diename(&temp_die)?:"<anonymous>");
+ return;
+ }
+
+ if (!dwarf_formref_die (&temp_attr, &temp_die))
+ {
+ clog << "\n Error in decoding type attribute for "
+ << (dwarf_diename(&temp_die)?:"<anonymous>");
+ return;
+ }
+
+ print_members(&temp_die,o);
+ }
+
+ if (dwarf_siblingof (die, &die_mem) != 0)
+ break;
+ }
+}
+
+
+bool
+dwflpp::find_struct_member(const string& member,
+ Dwarf_Die *parentdie,
+ const target_symbol *e,
+ Dwarf_Die *memberdie,
+ vector<Dwarf_Attribute>& locs)
+{
+ Dwarf_Attribute attr;
+ Dwarf_Die die = *parentdie;
+
+ switch (dwarf_child (&die, &die))
+ {
+ case 0: /* First child found. */
+ break;
+ case 1: /* No children. */
+ return false;
+ case -1: /* Error. */
+ default: /* Shouldn't happen */
+ throw semantic_error (string (dwarf_tag(&die) == DW_TAG_union_type ? "union" : "struct")
+ + string (dwarf_diename_integrate (&die) ?: "<anonymous>")
+ + string (dwarf_errmsg (-1)),
+ e->tok);
+ }
+
+ do
+ {
+ if (dwarf_tag(&die) != DW_TAG_member)
+ continue;
+
+ const char *name = dwarf_diename_integrate(&die);
+ if (name == NULL)
+ {
+ // need to recurse for anonymous structs/unions
+ Dwarf_Die subdie;
+
+ if (!dwarf_attr_integrate (&die, DW_AT_type, &attr) ||
+ !dwarf_formref_die (&attr, &subdie))
+ continue;
+
+ if (find_struct_member(member, &subdie, e, memberdie, locs))
+ goto success;
+ }
+ else if (name == member)
+ {
+ *memberdie = die;
+ goto success;
+ }
+ }
+ while (dwarf_siblingof (&die, &die) == 0);
+
+ return false;
+
+success:
+ /* As we unwind the recursion, we need to build the chain of
+ * locations that got to the final answer. */
+ if (dwarf_attr_integrate (&die, DW_AT_data_member_location, &attr))
+ locs.insert(locs.begin(), attr);
+
+ /* Union members don't usually have a location,
+ * but just use the containing union's location. */
+ else if (dwarf_tag(parentdie) != DW_TAG_union_type)
+ throw semantic_error ("no location for field '" + member
+ + "': " + string(dwarf_errmsg (-1)),
+ e->tok);
+
+ return true;
+}
+
+
+Dwarf_Die *
+dwflpp::translate_components(struct obstack *pool,
+ struct location **tail,
+ Dwarf_Addr pc,
+ const target_symbol *e,
+ Dwarf_Die *vardie,
+ Dwarf_Die *die_mem,
+ Dwarf_Attribute *attr_mem)
+{
+ Dwarf_Die *die = NULL;
+
+ unsigned i = 0;
+
+ if (vardie)
+ *die_mem = *vardie;
+
+ if (e->components.empty())
+ return die_mem;
+
+ while (i < e->components.size())
+ {
+ /* XXX: This would be desirable, but we don't get the target_symbol token,
+ and printing that gives us the file:line number too early anyway. */
+#if 0
+ // Emit a marker to note which field is being access-attempted, to give
+ // better error messages if deref() fails.
+ string piece = string(...target_symbol token...) + string ("#") + stringify(components[i].second);
+ obstack_printf (pool, "c->last_stmt = %s;", lex_cast_qstring(piece).c_str());
+#endif
+
+ die = die ? dwarf_formref_die (attr_mem, die_mem) : die_mem;
+ const int typetag = dwarf_tag (die);
+ switch (typetag)
+ {
+ case DW_TAG_typedef:
+ case DW_TAG_const_type:
+ case DW_TAG_volatile_type:
+ /* Just iterate on the referent type. */
+ break;
+
+ case DW_TAG_pointer_type:
+ if (e->components[i].first == target_symbol::comp_literal_array_index)
+ throw semantic_error ("cannot index pointer", e->tok);
+ // XXX: of course, we should support this the same way C does,
+ // by explicit pointer arithmetic etc. PR4166.
+
+ c_translate_pointer (pool, 1, 0 /* PR9768*/, die, tail);
+ break;
+
+ case DW_TAG_array_type:
+ if (e->components[i].first == target_symbol::comp_literal_array_index)
+ {
+ c_translate_array (pool, 1, 0 /* PR9768 */, die, tail,
+ NULL, lex_cast<Dwarf_Word>(e->components[i].second));
+ ++i;
+ }
+ else
+ throw semantic_error("bad field '"
+ + e->components[i].second
+ + "' for array type",
+ e->tok);
+ break;
+
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type:
+ if (dwarf_hasattr(die, DW_AT_declaration))
+ {
+ Dwarf_Die *tmpdie = dwflpp::declaration_resolve(dwarf_diename(die));
+ if (tmpdie == NULL)
+ throw semantic_error ("unresolved struct "
+ + string (dwarf_diename_integrate (die) ?: "<anonymous>"),
+ e->tok);
+ *die_mem = *tmpdie;
+ }
+
+ {
+ vector<Dwarf_Attribute> locs;
+ if (!find_struct_member(e->components[i].second, die, e, die, locs))
+ {
+ string alternatives;
+ stringstream members;
+ print_members(die, members);
+ if (members.str().size() != 0)
+ alternatives = " (alternatives:" + members.str();
+ throw semantic_error("unable to find member '" +
+ e->components[i].second + "' for struct "
+ + string(dwarf_diename_integrate(die) ?: "<unknown>")
+ + alternatives,
+ e->tok);
+ }
+
+ for (unsigned j = 0; j < locs.size(); ++j)
+ translate_location (pool, &locs[j], pc, NULL, tail, e);
+ }
+
+ ++i;
+ break;
+
+ case DW_TAG_enumeration_type:
+ throw semantic_error ("field '"
+ + e->components[i].second
+ + "' vs. enum type "
+ + string(dwarf_diename_integrate (die) ?: "<anonymous type>"),
+ e->tok);
+ break;
+ case DW_TAG_base_type:
+ throw semantic_error ("field '"
+ + e->components[i].second
+ + "' vs. base type "
+ + string(dwarf_diename_integrate (die) ?: "<anonymous type>"),
+ e->tok);
+ break;
+ case -1:
+ throw semantic_error ("cannot find type: " + string(dwarf_errmsg (-1)),
+ e->tok);
+ break;
+
+ default:
+ throw semantic_error (string(dwarf_diename_integrate (die) ?: "<anonymous type>")
+ + ": unexpected type tag "
+ + lex_cast<string>(dwarf_tag (die)),
+ e->tok);
+ break;
+ }
+
+ /* Now iterate on the type in DIE's attribute. */
+ if (dwarf_attr_integrate (die, DW_AT_type, attr_mem) == NULL)
+ throw semantic_error ("cannot get type of field: " + string(dwarf_errmsg (-1)), e->tok);
+ }
+ return die;
+}
+
+
+Dwarf_Die *
+dwflpp::resolve_unqualified_inner_typedie (Dwarf_Die *typedie_mem,
+ Dwarf_Attribute *attr_mem,
+ const target_symbol *e)
+{
+ Dwarf_Die *typedie;
+ int typetag = 0;
+ while (1)
+ {
+ typedie = dwarf_formref_die (attr_mem, typedie_mem);
+ if (typedie == NULL)
+ throw semantic_error ("cannot get type: " + string(dwarf_errmsg (-1)), e->tok);
+ typetag = dwarf_tag (typedie);
+ if (typetag != DW_TAG_typedef &&
+ typetag != DW_TAG_const_type &&
+ typetag != DW_TAG_volatile_type)
+ break;
+ if (dwarf_attr_integrate (typedie, DW_AT_type, attr_mem) == NULL)
+ throw semantic_error ("cannot get type of pointee: " + string(dwarf_errmsg (-1)), e->tok);
+ }
+ return typedie;
+}
+
+
+void
+dwflpp::translate_final_fetch_or_store (struct obstack *pool,
+ struct location **tail,
+ Dwarf_Addr module_bias,
+ Dwarf_Die *die,
+ Dwarf_Attribute *attr_mem,
+ bool lvalue,
+ const target_symbol *e,
+ string &,
+ string &,
+ exp_type & ty)
+{
+ /* First boil away any qualifiers associated with the type DIE of
+ the final location to be accessed. */
+
+ Dwarf_Die typedie_mem;
+ Dwarf_Die *typedie;
+ int typetag;
+ char const *dname;
+ string diestr;
+
+ typedie = resolve_unqualified_inner_typedie (&typedie_mem, attr_mem, e);
+ typetag = dwarf_tag (typedie);
+
+ /* Then switch behavior depending on the type of fetch/store we
+ want, and the type and pointer-ness of the final location. */
+
+ switch (typetag)
+ {
+ default:
+ dname = dwarf_diename(die);
+ diestr = (dname != NULL) ? dname : "<unknown>";
+ throw semantic_error ("unsupported type tag "
+ + lex_cast<string>(typetag)
+ + " for " + diestr, e->tok);
+ break;
+
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type:
+ dname = dwarf_diename(die);
+ diestr = (dname != NULL) ? dname : "<unknown>";
+ throw semantic_error ("struct/union '" + diestr
+ + "' is being accessed instead of a member of the struct/union", e->tok);
+ break;
+
+ case DW_TAG_enumeration_type:
+ case DW_TAG_base_type:
+
+ // Reject types we can't handle in systemtap
+ {
+ dname = dwarf_diename(die);
+ diestr = (dname != NULL) ? dname : "<unknown>";
+
+ Dwarf_Attribute encoding_attr;
+ Dwarf_Word encoding = (Dwarf_Word) -1;
+ dwarf_formudata (dwarf_attr_integrate (typedie, DW_AT_encoding, &encoding_attr),
+ & encoding);
+ if (encoding < 0)
+ {
+ // clog << "bad type1 " << encoding << " diestr" << endl;
+ throw semantic_error ("unsupported type (mystery encoding " + lex_cast<string>(encoding) + ")" +
+ " for " + diestr, e->tok);
+ }
+
+ if (encoding == DW_ATE_float
+ || encoding == DW_ATE_complex_float
+ /* XXX || many others? */)
+ {
+ // clog << "bad type " << encoding << " diestr" << endl;
+ throw semantic_error ("unsupported type (encoding " + lex_cast<string>(encoding) + ")" +
+ " for " + diestr, e->tok);
+ }
+ }
+
+ ty = pe_long;
+ if (lvalue)
+ c_translate_store (pool, 1, 0 /* PR9768 */, die, typedie, tail,
+ "THIS->value");
+ else
+ c_translate_fetch (pool, 1, 0 /* PR9768 */, die, typedie, tail,
+ "THIS->__retvalue");
+ break;
+
+ case DW_TAG_array_type:
+ case DW_TAG_pointer_type:
+
+ {
+ Dwarf_Die pointee_typedie_mem;
+ Dwarf_Die *pointee_typedie;
+ Dwarf_Word pointee_encoding;
+ Dwarf_Word pointee_byte_size = 0;
+
+ pointee_typedie = resolve_unqualified_inner_typedie (&pointee_typedie_mem, attr_mem, e);
+
+ if (dwarf_attr_integrate (pointee_typedie, DW_AT_byte_size, attr_mem))
+ dwarf_formudata (attr_mem, &pointee_byte_size);
+
+ dwarf_formudata (dwarf_attr_integrate (pointee_typedie, DW_AT_encoding, attr_mem),
+ &pointee_encoding);
+
+ if (lvalue)
+ {
+ ty = pe_long;
+ if (typetag == DW_TAG_array_type)
+ throw semantic_error ("cannot write to array address", e->tok);
+ assert (typetag == DW_TAG_pointer_type);
+ c_translate_pointer_store (pool, 1, 0 /* PR9768 */, typedie, tail,
+ "THIS->value");
+ }
+ else
+ {
+ // We have the pointer: cast it to an integral type via &(*(...))
+
+ // NB: per bug #1187, at one point char*-like types were
+ // automagically converted here to systemtap string values.
+ // For several reasons, this was taken back out, leaving
+ // pointer-to-string "conversion" (copying) to tapset functions.
+
+ ty = pe_long;
+ if (typetag == DW_TAG_array_type)
+ c_translate_array (pool, 1, 0 /* PR9768 */, typedie, tail, NULL, 0);
+ else
+ c_translate_pointer (pool, 1, 0 /* PR9768 */, typedie, tail);
+ c_translate_addressof (pool, 1, 0 /* PR9768 */, NULL, pointee_typedie, tail,
+ "THIS->__retvalue");
+ }
+ }
+ break;
+ }
+}
+
+
+string
+dwflpp::express_as_string (string prelude,
+ string postlude,
+ struct location *head)
+{
+ size_t bufsz = 1024;
+ char *buf = static_cast<char*>(malloc(bufsz));
+ assert(buf);
+
+ FILE *memstream = open_memstream (&buf, &bufsz);
+ assert(memstream);
+
+ fprintf(memstream, "{\n");
+ fprintf(memstream, "%s", prelude.c_str());
+ bool deref = c_emit_location (memstream, head, 1);
+ fprintf(memstream, "%s", postlude.c_str());
+ fprintf(memstream, " goto out;\n");
+
+ // dummy use of deref_fault label, to disable warning if deref() not used
+ fprintf(memstream, "if (0) goto deref_fault;\n");
+
+ // XXX: deref flag not reliable; emit fault label unconditionally
+ (void) deref;
+ fprintf(memstream,
+ "deref_fault:\n"
+ " goto out;\n");
+ fprintf(memstream, "}\n");
+
+ fclose (memstream);
+ string result(buf);
+ free (buf);
+ return result;
+}
+
+
+string
+dwflpp::literal_stmt_for_local (Dwarf_Die *scope_die,
+ Dwarf_Addr pc,
+ string const & local,
+ const target_symbol *e,
+ bool lvalue,
+ exp_type & ty)
+{
+ Dwarf_Die vardie;
+ Dwarf_Attribute fb_attr_mem, *fb_attr = NULL;
+
+ fb_attr = find_variable_and_frame_base (scope_die, pc, local, e,
+ &vardie, &fb_attr_mem);
+
+ if (sess.verbose>2)
+ clog << "finding location for local '" << local
+ << "' near address 0x" << hex << pc
+ << ", module bias 0x" << module_bias << dec
+ << "\n";
+
+ Dwarf_Attribute attr_mem;
+ if (dwarf_attr_integrate (&vardie, DW_AT_location, &attr_mem) == NULL)
+ {
+ throw semantic_error("failed to retrieve location "
+ "attribute for local '" + local
+ + "' (dieoffset: "
+ + lex_cast_hex<string>(dwarf_dieoffset (&vardie))
+ + ")",
+ e->tok);
+ }
+
+#define obstack_chunk_alloc malloc
+#define obstack_chunk_free free
+
+ struct obstack pool;
+ obstack_init (&pool);
+ struct location *tail = NULL;
+
+ /* Given $foo->bar->baz[NN], translate the location of foo. */
+
+ struct location *head = translate_location (&pool,
+ &attr_mem, pc, fb_attr, &tail,
+ e);
+
+ if (dwarf_attr_integrate (&vardie, DW_AT_type, &attr_mem) == NULL)
+ throw semantic_error("failed to retrieve type "
+ "attribute for local '" + local + "'",
+ e->tok);
+
+ /* Translate the ->bar->baz[NN] parts. */
+
+ Dwarf_Die die_mem, *die = dwarf_formref_die (&attr_mem, &die_mem);
+ die = translate_components (&pool, &tail, pc, e,
+ die, &die_mem, &attr_mem);
+
+ /* Translate the assignment part, either
+ x = $foo->bar->baz[NN]
+ or
+ $foo->bar->baz[NN] = x
+ */
+
+ string prelude, postlude;
+ translate_final_fetch_or_store (&pool, &tail, module_bias,
+ die, &attr_mem, lvalue, e,
+ prelude, postlude, ty);
+
+ /* Write the translation to a string. */
+ return express_as_string(prelude, postlude, head);
+}
+
+
+string
+dwflpp::literal_stmt_for_return (Dwarf_Die *scope_die,
+ Dwarf_Addr pc,
+ const target_symbol *e,
+ bool lvalue,
+ exp_type & ty)
+{
+ if (sess.verbose>2)
+ clog << "literal_stmt_for_return: finding return value for "
+ << (dwarf_diename(scope_die) ?: "<unknown>")
+ << "("
+ << (dwarf_diename(cu) ?: "<unknown>")
+ << ")\n";
+
+ struct obstack pool;
+ obstack_init (&pool);
+ struct location *tail = NULL;
+
+ /* Given $return->bar->baz[NN], translate the location of return. */
+ const Dwarf_Op *locops;
+ int nlocops = dwfl_module_return_value_location (module, scope_die,
+ &locops);
+ if (nlocops < 0)
+ {
+ throw semantic_error("failed to retrieve return value location"
+ " for "
+ + string(dwarf_diename(scope_die) ?: "<unknown>")
+ + "(" + string(dwarf_diename(cu) ?: "<unknown>")
+ + ")",
+ e->tok);
+ }
+ // the function has no return value (e.g. "void" in C)
+ else if (nlocops == 0)
+ {
+ throw semantic_error("function "
+ + string(dwarf_diename(scope_die) ?: "<unknown>")
+ + "(" + string(dwarf_diename(cu) ?: "<unknown>")
+ + ") has no return value",
+ e->tok);
+ }
+
+ struct location *head = c_translate_location (&pool, &loc2c_error, this,
+ &loc2c_emit_address,
+ 1, 0 /* PR9768 */,
+ pc, locops, nlocops,
+ &tail, NULL);
+
+ /* Translate the ->bar->baz[NN] parts. */
+
+ Dwarf_Attribute attr_mem;
+ if (dwarf_attr_integrate (scope_die, DW_AT_type, &attr_mem) == NULL)
+ throw semantic_error("failed to retrieve return value type attribute for "
+ + string(dwarf_diename(scope_die) ?: "<unknown>")
+ + "(" + string(dwarf_diename(cu) ?: "<unknown>")
+ + ")",
+ e->tok);
+
+ Dwarf_Die die_mem, *die = dwarf_formref_die (&attr_mem, &die_mem);
+ die = translate_components (&pool, &tail, pc, e,
+ die, &die_mem, &attr_mem);
+
+ /* Translate the assignment part, either
+ x = $return->bar->baz[NN]
+ or
+ $return->bar->baz[NN] = x
+ */
+
+ string prelude, postlude;
+ translate_final_fetch_or_store (&pool, &tail, module_bias,
+ die, &attr_mem, lvalue, e,
+ prelude, postlude, ty);
+
+ /* Write the translation to a string. */
+ return express_as_string(prelude, postlude, head);
+}
+
+
+string
+dwflpp::literal_stmt_for_pointer (Dwarf_Die *type_die,
+ const target_symbol *e,
+ bool lvalue,
+ exp_type & ty)
+{
+ if (sess.verbose>2)
+ clog << "literal_stmt_for_pointer: finding value for "
+ << (dwarf_diename(type_die) ?: "<unknown>")
+ << "("
+ << (dwarf_diename(cu) ?: "<unknown>")
+ << ")\n";
+
+ struct obstack pool;
+ obstack_init (&pool);
+ struct location *head = c_translate_argument (&pool, &loc2c_error, this,
+ &loc2c_emit_address,
+ 1, "THIS->pointer");
+ struct location *tail = head;
+
+ /* Translate the ->bar->baz[NN] parts. */
+
+ Dwarf_Attribute attr_mem;
+ Dwarf_Die die_mem, *die = NULL;
+ die = translate_components (&pool, &tail, 0, e,
+ type_die, &die_mem, &attr_mem);
+
+ /* Translate the assignment part, either
+ x = (THIS->pointer)->bar->baz[NN]
+ or
+ (THIS->pointer)->bar->baz[NN] = x
+ */
+
+ string prelude, postlude;
+ translate_final_fetch_or_store (&pool, &tail, module_bias,
+ die, &attr_mem, lvalue, e,
+ prelude, postlude, ty);
+
+ /* Write the translation to a string. */
+ return express_as_string(prelude, postlude, head);
+}
+
+
+/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */