/* * Copyright (c) 2014-2015, Chris Johns * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * @file * * @ingroup rtems_rld * * @brief RTEMS Trace Linker manages creating a tracable RTEMS executable. * */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef HAVE_KILL #define kill(p,s) raise(s) #endif namespace rld { /** * RTEMS Trace Linker. */ namespace trace { /** * Dump on error user option. */ bool dump_on_error; /** * A container of arguments. */ typedef std::vector < std::string > function_args; /** * The return value. */ typedef std::string function_return; /** * An option is a name and value pair. We consider options as global. */ struct option { std::string name; /**< The name of this option. */ std::string value; /**< The option's value.. */ /** * Load the option. */ option (const std::string& name, const std::string& value) : name (name), value (value) { } }; /** * A container of options. */ typedef std::vector < option > options; /** * A function's signature. */ struct signature { std::string name; /**< The function's name. */ function_args args; /**< The function's list of arguments. */ function_return ret; /**< The fuctions return value. */ /** * The default constructor. */ signature (); /** * Construct the signature loading it from the configuration. */ signature (const rld::config::record& record); /** * Has the signature got a return value ? */ bool has_ret () const; /** * Has the signature got any arguments ? */ bool has_args () const; /** * Return the function's declaration. */ const std::string decl (const std::string& prefix = "") const; }; /** * A container of signatures. */ typedef std::map < std::string, signature > signatures; /** * A function is list of function signatures headers and defines that allow * a function to be wrapped. */ struct function { std::string name; /**< The name of this wrapper. */ rld::strings headers; /**< Include statements. */ rld::strings defines; /**< Define statements. */ signatures signatures_; /**< Signatures in this function. */ /** * Load the function. */ function (rld::config::config& config, const std::string& name); /** * Dump the function. */ void dump (std::ostream& out) const; }; /** * A container of functions. */ typedef std::vector < function > functions; /** * A generator and that contains the functions used to trace arguments and * return values. It also provides the implementation of those functions. */ struct generator { std::string name; /**< The name of this wrapper. */ std::string lock_model; /**< The lock model if provided, default "alloc". */ std::string lock_local; /**< Code template to declare a local lock variable. */ std::string lock_acquire; /**< The lock acquire if provided. */ std::string lock_release; /**< The lock release if provided. */ std::string buffer_local; /**< Code template to declare a local buffer variable. */ rld::strings headers; /**< Include statements. */ rld::strings defines; /**< Define statements. */ std::string entry_trace; /**< Code template to trace the function entry. */ std::string entry_alloc; /**< Code template to perform a buffer allocation. */ std::string arg_trace; /**< Code template to trace an argument. */ std::string exit_trace; /**< Code template to trace the function exit. */ std::string exit_alloc; /**< Code template to perform a buffer allocation. */ std::string ret_trace; /**< Code template to trace the return value. */ rld::strings code; /**< Code block inserted before the trace code. */ /** * Default constructor. */ generator (); /** * Load the generator. */ generator (rld::config::config& config, const std::string& name); /** * Dump the generator. */ void dump (std::ostream& out) const; }; /** * Tracer. */ class tracer { public: tracer (); /** * Load the user's configuration. */ void load (rld::config::config& config, const std::string& section); /** * Process any script based options. */ void load_options (rld::config::config& config, const rld::config::section& section); /** * The defines for the trace. */ void load_defines (rld::config::config& config, const rld::config::section& section); /** * The functions for the trace. */ void load_functions (rld::config::config& config, const rld::config::section& section); /** * The enables for the tracer. */ void load_enables (rld::config::config& config, const rld::config::section& section); /** * The triggers for the tracer. */ void load_triggers (rld::config::config& config, const rld::config::section& section); /** * The traces for the tracer. */ void load_traces (rld::config::config& config, const rld::config::section& section); /** * Get option from the options section. */ const std::string get_option (const std::string& name) const; /** * Generate the wrapper object file. */ void generate (rld::process::tempfile& c); /** * Generate the trace names as a string table. */ void generate_names (rld::process::tempfile& c); /** * Generate the trace signature tables. */ void generate_signatures (rld::process::tempfile& c); /** * Generate the enabled trace bitmap. */ void generate_enables (rld::process::tempfile& c); /** * Generate the triggered trace bitmap. */ void generate_triggers (rld::process::tempfile& c); /** * Generate the functions. */ void generate_functions (rld::process::tempfile& c); /** * Generate the trace functions. */ void generate_traces (rld::process::tempfile& c); /** * Generate a bitmap. */ void generate_bitmap (rld::process::tempfile& c, const rld::strings& names, const std::string& label, const bool global_set); /** * Function macro replace. */ void macro_func_replace (std::string& text, const signature& sig, const std::string& index); /** * Find the function given a name. */ const function& find_function (const std::string& name) const; /** * Get the traces. */ const rld::strings& get_traces () const; /** * Dump the wrapper. */ void dump (std::ostream& out) const; private: std::string name; /**< The name of the trace. */ rld::strings defines; /**< Define statements. */ rld::strings enables; /**< The default enabled functions. */ rld::strings triggers; /**< The default trigger functions. */ rld::strings traces; /**< The functions to trace. */ options options_; /**< The options. */ functions functions_; /**< The functions that can be traced. */ generator generator_; /**< The tracer's generator. */ }; /** * Trace Linker. */ class linker { public: linker (); /** * Load the user's configuration. */ void load_config (const std::string& name, const std::string& trace, const std::string& path); /** * Generate the C file. */ void generate_wrapper (rld::process::tempfile& c); /** * Compile the C file. */ void compile_wrapper (rld::process::tempfile& c, rld::process::tempfile& o); /** * Link the application. */ void link (rld::process::tempfile& o, const std::string& ld_cmds); /** * Dump the linker. */ void dump (std::ostream& out) const; private: rld::config::config config; /**< User configuration. */ tracer tracer_; /**< The tracer */ rld::process::tempfile c; /**< The C wrapper file */ rld::process::tempfile o; /**< The wrapper object file */ }; /** * Recursive parser for strings. */ void parse (rld::config::config& config, const rld::config::section& section, const std::string& sec_name, const std::string& rec_name, rld::strings& items, bool split = true, int depth = 0) { if (depth > 32) throw rld::error ("too deep", "parsing: " + sec_name + '/' + rec_name); rld::config::parse_items (section, rec_name, items, false, false, split); rld::strings sl; rld::config::parse_items (section, sec_name, sl); for (rld::strings::iterator sli = sl.begin (); sli != sl.end (); ++sli) { const rld::config::section& sec = config.get_section (*sli); parse (config, sec, sec_name, rec_name, items, split, depth + 1); } /* * Make the items unique. */ rld::strings::iterator ii; ii = std::unique (items.begin (), items.end ()); items.resize (std::distance (items.begin (), ii)); } signature::signature () { } signature::signature (const rld::config::record& record) { /* * There can only be one function signature in the configuration. */ if (!record.single ()) throw rld::error ("duplicate", "signature: " + record.name); name = record.name; /* * Signatures are defined as the return value then the arguments * delimited by a comma and white space. No checking is made of the * return value or arguments. */ rld::strings si; rld::config::parse_items (record, si); if (si.size () == 0) throw rld::error ("no return value", "signature: " + name); if (si.size () == 1) throw rld::error ("no arguments", "signature: " + name); ret = si[0]; args.resize (si.size () - 1); std::copy (si.begin () + 1, si.end (), args.begin ()); } bool signature::has_ret () const { /* * @todo Need to define as part of the function signature if ret * processing is required. */ return ret != "void"; } bool signature::has_args () const { if (args.empty ()) return false; return ((args.size() == 1) && (args[0] == "void")) ? false : true; } const std::string signature::decl (const std::string& prefix) const { std::string ds = ret + ' ' + prefix + name + '('; if (has_args ()) { int arg = 0; for (function_args::const_iterator ai = args.begin (); ai != args.end (); ++ai) { if (ai != args.begin ()) ds += ", "; ds += (*ai) + " a" + rld::to_string (++arg); } } else { ds += "void"; } ds += ')'; return ds; } function::function (rld::config::config& config, const std::string& name) : name (name) { /* * A function section optionally contain one or more records of: * * # headers A list of sections containing headers or header records. * # header A list of include string that are single or double quoted. * # defines A list of sections containing defines or define record. * # defines A list of define string that are single or double quoted. * # signatures A list of section names of signatures. * # includes A list of files to include. * * @note The quoting and list spliting is a little weak because a delimiter * in a quote should not be seen as a delimiter. */ const rld::config::section& section = config.get_section (name); config.includes (section); parse (config, section, "headers", "header", headers); parse (config, section, "defines", "define", defines); rld::strings sig_list; section.get_record_items ("signatures", sig_list); for (rld::strings::const_iterator sli = sig_list.begin (); sli != sig_list.end (); ++sli) { rld::strings sigs; rld::split(sigs, *sli, ','); for (rld::strings::const_iterator ssi = sigs.begin (); ssi != sigs.end (); ++ssi) { const rld::config::section& sig_sec = config.get_section (*ssi); for (rld::config::records::const_iterator si = sig_sec.recs.begin (); si != sig_sec.recs.end (); ++si) { signature sig (*si); signatures_[sig.name] = sig; } } } } void function::dump (std::ostream& out) const { out << " Function: " << name << std::endl << " Headers: " << headers.size () << std::endl; for (rld::strings::const_iterator hi = headers.begin (); hi != headers.end (); ++hi) { out << " " << (*hi) << std::endl; } out << " Defines: " << defines.size () << std::endl; for (rld::strings::const_iterator di = defines.begin (); di != defines.end (); ++di) { out << " " << (*di) << std::endl; } out << " Signatures: " << signatures_.size () << std::endl; for (signatures::const_iterator si = signatures_.begin (); si != signatures_.end (); ++si) { const signature& sig = (*si).second; out << " " << sig.name << ": " << sig.decl () << ';' << std::endl; } } generator::generator () { } generator::generator (rld::config::config& config, const std::string& name) : name (name) { /* * A generator section optionally contain one or more records of: * * # headers A list of sections containing headers or header records. * # header A list of include string that are single or double quoted. * # defines A list of sections containing defines or define record. * # define A list of define string that are single or double quoted. * # entry-trace The wrapper call made on a function's entry. Returns `bool * where `true` is the function is being traced. This call is made * without the lock being held if a lock is defined. * # arg-trace The wrapper call made for each argment to the trace function if * the function is being traced. This call is made without the * lock being held if a lock is defined. * # exit-trace The wrapper call made after a function's exit. Returns `bool * where `true` is the function is being traced. This call is made * without the lock being held if a lock is defined. * # ret-trace The wrapper call made to log the return value if the funciton * is being traced. This call is made without the lock being held * if a lock is defined. * # lock-model The wrapper code lock model. Models are 'alloc', or capture'. * # lock-local The wrapper code to declare a local lock variable. * # lock-acquire The wrapper code to acquire the lock. * # lock-release The wrapper code to release the lock. * # buffer-local The wrapper code to declare a buffer index local variable. * # buffer-alloc The wrapper call made with a lock held if defined to allocate * buffer space to hold the trace data. A suitable 32bit buffer * index is returned. If there is no space an invalid index is * returned. The generator must handle any overhead space needed. * the generator needs to make sure the space is available before * making the alloc all. * # code-blocks A list of code blcok section names. * # code A code block in `< " << rld::find_replace (*ci, "\n", "\n | ") << std::endl; } } tracer::tracer () { } void tracer::load (rld::config::config& config, const std::string& tname) { /* * The configuration must contain a "section" section. This is the top level * configuration and may contain: * * # name The name of trace being linked. * # options A list of option sections. * # defines A list of sections containing defines or define record. * # define A list of define string that are single or double quoted. * # enables The list of sections containing enabled functions to trace. * # triggers The list of sections containing enabled functions to trigger trace on. * # traces The list of sections containing function lists to trace. * # functions The list of sections containing function details. * # include The list of files to include. * * The following records are required: * * # name * # traces * # functions */ const rld::config::section& section = config.get_section (tname); name = section.get_record_item ("name"); load_options (config, section); config.includes (section); load_defines (config, section); load_functions (config, section); load_enables (config, section); load_triggers (config, section); load_traces (config, section); } void tracer::load_options (rld::config::config& config, const rld::config::section& section) { rld::strings opts; rld::config::parse_items (section, "options", opts, false, true, true); if (rld::verbose ()) std::cout << "options: " << section.name << ": " << opts.size () << std::endl; options_.clear (); for (rld::strings::const_iterator osi = opts.begin (); osi != opts.end (); ++osi) { const rld::config::section& osec = config.get_section (*osi); if (rld::verbose ()) std::cout << " options: " << osec.name << ": recs:" << osec.recs.size () << std::endl; for (rld::config::records::const_iterator ori = osec.recs.begin (); ori != osec.recs.end (); ++ori) { const rld::config::record& opt = *ori; if (!opt.single ()) throw rld::error ("mode than one option specified", "option: " + opt.name); options_.push_back (option (opt.name, opt[0])); if (opt.name == "dump-on-error") { dump_on_error = true; } else if (opt.name == "verbose") { int level = ::strtoul(opt[0].c_str (), 0, 0); if (level == 0) level = 1; for (int l = 0; l < level; ++l) rld::verbose_inc (); } else if (opt.name == "prefix") { rld::cc::set_exec_prefix (opt[0]); } else if (opt.name == "cc") { rld::cc::set_cc (opt[0]); } else if (opt.name == "ld") { rld::cc::set_ld (opt[0]); } else if (opt.name == "cflags") { rld::cc::append_flags (opt[0], rld::cc::ft_cflags); } else if (opt.name == "rtems-path") { rld::rtems::set_path(opt[0]); } else if (opt.name == "rtems-bsp") { rld::rtems::set_arch_bsp(opt[0]); } } } } void tracer::load_defines (rld::config::config& config, const rld::config::section& section) { parse (config, section, "defines", "define", defines); } void tracer::load_functions (rld::config::config& config, const rld::config::section& section) { rld::strings fl; rld::config::parse_items (section, "functions", fl, true); for (rld::strings::const_iterator fli = fl.begin (); fli != fl.end (); ++fli) { functions_.push_back (function (config, *fli)); } } void tracer::load_enables (rld::config::config& config, const rld::config::section& section) { parse (config, section, "enables", "enable", enables); } void tracer::load_triggers (rld::config::config& config, const rld::config::section& section) { parse (config, section, "triggers", "trigger", triggers); } void tracer::load_traces (rld::config::config& config, const rld::config::section& section) { parse (config, section, "traces", "trace", traces); rld::strings gens; std::string gen; parse (config, section, "traces", "generator", gens); if (gens.size () > 1) { if (dump_on_error) dump (std::cout); throw rld::error ("duplicate generators", "tracer: " + section.name); } if (gens.size () == 0) { const rld::config::section& dg_section = config.get_section ("default-generator"); gen = dg_section.get_record_item ("generator"); config.includes (dg_section); } else { gen = gens[0]; } sort (traces.begin (), traces.end ()); generator_ = generator (config, gen); } const std::string tracer::get_option (const std::string& name) const { std::string value; for (options::const_iterator oi = options_.begin (); oi != options_.end (); ++oi) { const option& opt = *oi; if (opt.name == name) { value = opt.value; break; } } return value; } void tracer::generate (rld::process::tempfile& c) { c.open (true); if (rld::verbose ()) std::cout << "wrapper C file: " << c.name () << std::endl; try { c.write_line ("/*"); c.write_line (" * RTEMS Trace Linker Wrapper"); c.write_line (" * Automatically generated."); c.write_line (" */"); c.write_line (""); c.write_line ("/*"); c.write_line (" * Tracer: " + name); c.write_line (" */"); c.write_lines (defines); c.write_line (""); c.write_line ("/*"); c.write_line (" * Generator: " + generator_.name); c.write_line (" */"); c.write_lines (generator_.defines); c.write_lines (generator_.headers); c.write_line (""); generate_functions (c); generate_names (c); generate_signatures (c); generate_enables (c); generate_triggers (c); c.write_line (""); c.write_lines (generator_.code); generate_traces (c); } catch (...) { c.close (); if (dump_on_error) dump (std::cout); throw; } c.close (); if (rld::verbose (RLD_VERBOSE_DETAILS)) { std::cout << "Generated C file:" << std::endl; c.output (" ", std::cout, true); } } void tracer::generate_names (rld::process::tempfile& c) { const std::string opt = get_option ("gen-names"); if (opt == "disable") return; c.write_line (""); c.write_line ("/*"); c.write_line (" * Names."); c.write_line (" */"); std::stringstream sss; sss << "uint32_t __rtld_trace_names_size = " << traces.size() << ";" << std::endl << "const char const* __rtld_trace_names[" << traces.size() << "] = " << std::endl << "{"; c.write_line (sss.str ()); int count = 0; for (rld::strings::const_iterator ti = traces.begin (); ti != traces.end (); ++ti) { const std::string& trace = *ti; sss.str (std::string ()); sss << " /* " << std::setw (3) << count << " */ \"" << trace << "\","; c.write_line (sss.str ()); ++count; } c.write_line ("};"); } void tracer::generate_signatures (rld::process::tempfile& c) { const std::string opt = get_option ("gen-sigs"); if (opt == "disable") return; c.write_line (""); c.write_line ("/*"); c.write_line (" * Signatures."); c.write_line (" */"); c.write_line (""); c.write_line ("typedef struct {"); c.write_line (" uint32_t size;"); c.write_line (" const char* const type;"); c.write_line ("} __rtld_trace_sig_arg;"); c.write_line (""); c.write_line ("typedef struct {"); c.write_line (" uint32_t argc;"); c.write_line (" const __rtld_trace_sig_arg* args;"); c.write_line ("} __rtld_trace_sig;"); c.write_line (""); std::stringstream sss; for (rld::strings::const_iterator ti = traces.begin (); ti != traces.end (); ++ti) { const std::string& trace = *ti; bool found = false; for (functions::const_iterator fi = functions_.begin (); !found && (fi != functions_.end ()); ++fi) { const function& funcs = *fi; signatures::const_iterator si = funcs.signatures_.find (trace); if (si != funcs.signatures_.end ()) { found = true; const signature& sig = (*si).second; size_t argc = 1 + (sig.args.size () == 0 ? 1 : sig.args.size ()); sss.str (std::string ()); sss << "const __rtld_trace_sig_arg __rtld_trace_sig_args_" << trace << "[" << argc << "] =" << std::endl << "{" << std::endl; if (sig.has_ret ()) sss << " { sizeof (" << sig.ret << "), \"" << sig.ret << "\" }," << std::endl; else sss << " { 0, \"void\" }," << std::endl; if (sig.has_args ()) { for (size_t a = 0; a < sig.args.size (); ++a) { sss << " { sizeof (" << sig.args[a] << "), \"" << sig.args[a] << "\" }," << std::endl; } } else sss << " { 0, \"void\" }," << std::endl; sss << "};" << std::endl; c.write_line (sss.str ()); } } if (!found) throw rld::error ("not found", "trace function: " + trace); } sss.str (std::string ()); sss << "const __rtld_trace_sig __rtld_trace_signatures[" << traces.size () << "] = " << "{" << std::endl; for (rld::strings::const_iterator ti = traces.begin (); ti != traces.end (); ++ti) { const std::string& trace = *ti; for (functions::const_iterator fi = functions_.begin (); fi != functions_.end (); ++fi) { const function& funcs = *fi; signatures::const_iterator si = funcs.signatures_.find (trace); if (si != funcs.signatures_.end ()) { const signature& sig = (*si).second; size_t argc = 1 + (sig.args.size () == 0 ? 1 : sig.args.size ()); sss << " { " << argc << ", __rtld_trace_sig_args_" << trace << " }," << std::endl; break; } } } sss << "};" << std::endl; c.write_line (sss.str ()); } void tracer::generate_enables (rld::process::tempfile& c) { const std::string opt = get_option ("gen-enables"); bool global_state = false; if (opt == "disable") return; if (opt == "global-on") global_state = true; c.write_line (""); c.write_line ("/*"); c.write_line (" * Enables."); c.write_line (" */"); generate_bitmap (c, enables, "enables", global_state); } void tracer::generate_triggers (rld::process::tempfile& c) { const std::string opt = get_option ("gen-triggers"); bool global_state = false; if (opt == "disable") return; if (opt == "global-on") global_state = true; c.write_line (""); c.write_line ("/*"); c.write_line (" * Triggers."); c.write_line (" */"); generate_bitmap (c, triggers, "triggers", global_state); c.write_line (""); } void tracer::generate_functions (rld::process::tempfile& c) { c.write_line ("/*"); c.write_line (" * Functions."); c.write_line (" */"); for (functions::const_iterator fi = functions_.begin (); fi != functions_.end (); ++fi) { const function& funcs = *fi; for (rld::strings::const_iterator ti = traces.begin (); ti != traces.end (); ++ti) { const std::string& trace = *ti; signatures::const_iterator si = funcs.signatures_.find (trace); if (si != funcs.signatures_.end ()) { c.write_line (""); c.write_line ("/*"); c.write_line (" * Function: " + funcs.name); c.write_line (" */"); c.write_lines (funcs.defines); c.write_lines (funcs.headers); break; } } } } void tracer::generate_traces (rld::process::tempfile& c) { c.write_line ("/*"); c.write_line (" * Wrappers."); c.write_line (" */"); size_t count = 0; for (rld::strings::const_iterator ti = traces.begin (); ti != traces.end (); ++ti) { const std::string& trace = *ti; bool found = false; for (functions::const_iterator fi = functions_.begin (); fi != functions_.end (); ++fi) { const function& funcs = *fi; signatures::const_iterator si = funcs.signatures_.find (trace); if (si != funcs.signatures_.end ()) { found = true; const signature& sig = (*si).second; std::stringstream lss; lss << count; c.write_line(""); std::string ds; std::string des; std::string drs; bool ds_added = false; bool des_added = false; bool drs_added = false; ds = "#define FUNC_DATA_SIZE_" + sig.name + " ("; des = "#define FUNC_DATA_ENTRY_SIZE_" + sig.name + " ("; drs = "#define FUNC_DATA_RET_SIZE_" + sig.name + " ("; if (sig.has_args ()) { for (size_t a = 0; a < sig.args.size (); ++a) { if (ds_added) ds += " + "; else ds_added = true; if (des_added) des += " + "; else des_added = true; ds += "sizeof(" + sig.args[a] + ')'; des += "sizeof(" + sig.args[a] + ')'; } } if (sig.has_ret () && !generator_.ret_trace.empty ()) { if (ds_added) ds += " + "; else ds_added = true; drs_added = true; ds += "sizeof(" + sig.ret + ')'; drs += "sizeof(" + sig.ret + ')'; } if (!ds_added) ds += '0'; ds += ')'; if (!des_added) des += '0'; des += ')'; if (!drs_added) drs += '0'; drs += ')'; c.write_line(ds); c.write_line(des); c.write_line(drs); c.write_line(sig.decl () + ";"); c.write_line(sig.decl ("__real_") + ";"); c.write_line(sig.decl ("__wrap_")); c.write_line("{"); if (!generator_.lock_local.empty ()) c.write_line(generator_.lock_local); if (!generator_.buffer_local.empty ()) c.write_line(generator_.buffer_local); if (sig.has_ret ()) c.write_line(" " + sig.ret + " ret;"); std::string l; if (!generator_.lock_acquire.empty ()) c.write_line(generator_.lock_acquire); if (!generator_.entry_alloc.empty ()) { l = " " + generator_.entry_alloc; macro_func_replace (l, sig, lss.str ()); c.write_line(l); } if (!generator_.lock_release.empty () && (generator_.lock_model.empty () || (generator_.lock_model == "alloc"))) c.write_line(generator_.lock_release); if (!generator_.entry_trace.empty ()) { l = " " + generator_.entry_trace; macro_func_replace (l, sig, lss.str ()); c.write_line(l); } if (sig.has_args ()) { for (size_t a = 0; a < sig.args.size (); ++a) { std::string n = rld::to_string ((int) (a + 1)); l = " " + generator_.arg_trace; l = rld::find_replace (l, "@ARG_NUM@", n); l = rld::find_replace (l, "@ARG_TYPE@", '"' + sig.args[a] + '"'); l = rld::find_replace (l, "@ARG_SIZE@", "sizeof(" + sig.args[a] + ')'); l = rld::find_replace (l, "@ARG_LABEL@", "a" + n); c.write_line(l); } } if (!generator_.lock_release.empty () && generator_.lock_model == "trace") c.write_line(generator_.lock_release); l.clear (); if (sig.has_ret ()) l = " ret ="; l += " __real_" + sig.name + '('; if (sig.has_args ()) { for (size_t a = 0; a < sig.args.size (); ++a) { if (a) l += ", "; l += "a" + rld::to_string ((int) (a + 1)); } } l += ");"; c.write_line(l); if (!generator_.lock_acquire.empty ()) c.write_line(generator_.lock_acquire); if (!generator_.exit_alloc.empty ()) { l = " " + generator_.exit_alloc; macro_func_replace (l, sig, lss.str ()); c.write_line(l); } if (!generator_.lock_release.empty () && (generator_.lock_model.empty () || (generator_.lock_model == "alloc"))) c.write_line(generator_.lock_release); if (!generator_.exit_trace.empty ()) { l = " " + generator_.exit_trace; macro_func_replace (l, sig, lss.str ()); c.write_line(l); } if (sig.has_ret () && !generator_.ret_trace.empty ()) { std::string l = " " + generator_.ret_trace; l = rld::find_replace (l, "@RET_TYPE@", '"' + sig.ret + '"'); l = rld::find_replace (l, "@RET_SIZE@", "sizeof(" + sig.ret + ')'); l = rld::find_replace (l, "@RET_LABEL@", "ret"); c.write_line(l); } if (!generator_.lock_release.empty ()) c.write_line(generator_.lock_release); if (sig.has_ret ()) c.write_line(" return ret;"); c.write_line("}"); } } if (!found) throw rld::error ("not found", "trace function: " + trace); ++count; } } void tracer::generate_bitmap (rld::process::tempfile& c, const rld::strings& names, const std::string& label, const bool global_set) { uint32_t bitmap_size = ((traces.size () - 1) / (4 * 8)) + 1; std::stringstream ss; ss << "uint32_t __rtld_trace_" << label << "_size = " << traces.size() << ";" << std::endl << "const uint32_t __rtld_trace_" << label << "[" << bitmap_size << "] = " << std::endl << "{" << std::endl; size_t count = 0; size_t bit = 0; uint32_t bitmask = 0; for (rld::strings::const_iterator ti = traces.begin (); ti != traces.end (); ++ti) { const std::string& trace = *ti; bool set = global_set; if (!global_set) { for (rld::strings::const_iterator ni = names.begin (); ni != names.end (); ++ni) { const std::string& name = *ni; if (trace == name) set = true; } } if (set) bitmask |= 1 << bit; ++bit; ++count; if ((bit >= 32) || (count >= traces.size ())) { ss << " 0x" << std::hex << std::setfill ('0') << std::setw (8) << bitmask << ','; if ((count % 4) == 0) ss << std::endl; bit = 0; bitmask = 0; } } c.write_line (ss.str ()); c.write_line ("};"); } void tracer::macro_func_replace (std::string& text, const signature& sig, const std::string& index) { text = rld::find_replace (text, "@FUNC_NAME@", '"' + sig.name + '"'); text = rld::find_replace (text, "@FUNC_INDEX@", index); text = rld::find_replace (text, "@FUNC_LABEL@", sig.name); text = rld::find_replace (text, "@FUNC_DATA_SIZE@", "FUNC_DATA_SIZE_" + sig.name); text = rld::find_replace (text, "@FUNC_DATA_ENTRY_SIZE@", "FUNC_DATA_ENTRY_SIZE_" + sig.name); text = rld::find_replace (text, "@FUNC_DATA_RET_SIZE@", "FUNC_DATA_RET_SIZE_" + sig.name); } const rld::strings& tracer::get_traces () const { return traces; } void tracer::dump (std::ostream& out) const { out << " Tracer: " << name << std::endl << " Traces: " << traces.size () << std::endl; for (rld::strings::const_iterator ti = traces.begin (); ti != traces.end (); ++ti) { out << " " << (*ti) << std::endl; } out << " Functions: " << functions_.size () << std::endl; for (functions::const_iterator fi = functions_.begin (); fi != functions_.end (); ++fi) { (*fi).dump (out); } out << " Generator: " << std::endl; generator_.dump (out); } linker::linker () { } void linker::load_config (const std::string& name, const std::string& trace, const std::string& path) { std::string sp = rld::get_prefix (); rld::path::path_join (sp, "share", sp); rld::path::path_join (sp, "rtems", sp); rld::path::path_join (sp, "trace-linker", sp); if (!path.empty ()) sp = path + ':' + sp; if (rld::verbose ()) std::cout << "search path: " << sp << std::endl; config.set_search_path (sp); config.clear (); config.load (name); tracer_.load (config, trace); } void linker::generate_wrapper (rld::process::tempfile& c) { tracer_.generate (c); } void linker::compile_wrapper (rld::process::tempfile& c, rld::process::tempfile& o) { rld::process::arg_container args; if (rld::verbose ()) std::cout << "wrapper O file: " << o.name () << std::endl; rld::cc::make_cc_command (args); rld::cc::append_flags (rld::cc::ft_cflags, args); args.push_back ("-O2"); args.push_back ("-g"); args.push_back ("-c"); args.push_back ("-o"); args.push_back (o.name ()); args.push_back (c.name ()); rld::process::tempfile out; rld::process::tempfile err; rld::process::status status; status = rld::process::execute (rld::cc::get_cc (), args, out.name (), err.name ()); if ((status.type != rld::process::status::normal) || (status.code != 0)) { err.output (rld::cc::get_cc (), std::cout); if (dump_on_error) dump (std::cout); throw rld::error ("Compiler error", "compiling wrapper"); } } void linker::link (rld::process::tempfile& o, const std::string& ld_cmd) { rld::process::arg_container args; if (rld::verbose ()) std::cout << "linking: " << o.name () << std::endl; std::string wrap = " -Wl,--wrap="; rld::cc::make_ld_command (args); rld::process::args_append (args, wrap + rld::join (tracer_.get_traces (), wrap)); args.push_back (o.name ()); rld::process::args_append (args, ld_cmd); rld::process::tempfile out; rld::process::tempfile err; rld::process::status status; status = rld::process::execute (rld::cc::get_ld (), args, out.name (), err.name ()); if ((status.type != rld::process::status::normal) || (status.code != 0)) { err.output (rld::cc::get_ld (), std::cout); throw rld::error ("Linker error", "linking"); } err.output (rld::cc::get_ld (), std::cout); } void linker::dump (std::ostream& out) const { const rld::config::paths& cpaths = config.get_paths (); out << " Configuration Files: " << cpaths.size () << std::endl; for (rld::config::paths::const_iterator pi = cpaths.begin (); pi != cpaths.end (); ++pi) { out << " " << (*pi) << std::endl; } tracer_.dump (out); } } } /** * RTEMS Trace Linker options. This needs to be rewritten to be like cc where * only a single '-' and long options is present. Anything after '--' is passed * to RTEMS's real linker. */ static struct option rld_opts[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, { "verbose", no_argument, NULL, 'v' }, { "warn", no_argument, NULL, 'w' }, { "keep", no_argument, NULL, 'k' }, { "compiler", required_argument, NULL, 'c' }, { "linker", required_argument, NULL, 'l' }, { "exec-prefix", required_argument, NULL, 'E' }, { "cflags", required_argument, NULL, 'f' }, { "rtems", required_argument, NULL, 'r' }, { "rtems-bsp", required_argument, NULL, 'B' }, { "config", required_argument, NULL, 'C' }, { "path", required_argument, NULL, 'P' }, { "wrapper", required_argument, NULL, 'W' }, { NULL, 0, NULL, 0 } }; void usage (int exit_code) { std::cout << "rtems-trace-ld [options] objects" << std::endl << "Options and arguments:" << std::endl << " -h : help (also --help)" << std::endl << " -V : print linker version number and exit (also --version)" << std::endl << " -v : verbose (trace import parts), can supply multiple times" << std::endl << " to increase verbosity (also --verbose)" << std::endl << " -w : generate warnings (also --warn)" << std::endl << " -k : keep temporary files (also --keep)" << std::endl << " -c compiler : target compiler is not standard (also --compiler)" << std::endl << " -l linker : target linker is not standard (also --linker)" << std::endl << " -E prefix : the RTEMS tool prefix (also --exec-prefix)" << std::endl << " -f cflags : C compiler flags (also --cflags)" << std::endl << " -r path : RTEMS path (also --rtems)" << std::endl << " -B bsp : RTEMS arch/bsp (also --rtems-bsp)" << std::endl << " -W wrapper : wrapper file name without ext (also --wrapper)" << std::endl << " -C ini : user configuration INI file (also --config)" << std::endl << " -P path : user configuration file search path (also --path)" << std::endl; ::exit (exit_code); } static void fatal_signal (int signum) { signal (signum, SIG_DFL); rld::process::temporaries_clean_up (); /* * Get the same signal again, this time not handled, so its normal effect * occurs. */ kill (getpid (), signum); } static void setup_signals (void) { if (signal (SIGINT, SIG_IGN) != SIG_IGN) signal (SIGINT, fatal_signal); #ifdef SIGHUP if (signal (SIGHUP, SIG_IGN) != SIG_IGN) signal (SIGHUP, fatal_signal); #endif if (signal (SIGTERM, SIG_IGN) != SIG_IGN) signal (SIGTERM, fatal_signal); #ifdef SIGPIPE if (signal (SIGPIPE, SIG_IGN) != SIG_IGN) signal (SIGPIPE, fatal_signal); #endif #ifdef SIGCHLD signal (SIGCHLD, SIG_DFL); #endif } int main (int argc, char* argv[]) { int ec = 0; setup_signals (); try { rld::trace::linker linker; std::string cc; std::string ld; std::string ld_cmd; std::string configuration; std::string path; std::string trace = "tracer"; std::string wrapper; std::string rtems_path; std::string rtems_arch_bsp; rld::set_cmdline (argc, argv); while (true) { int opt = ::getopt_long (argc, argv, "hvwkVc:l:E:f:C:P:r:B:W:", rld_opts, NULL); if (opt < 0) break; switch (opt) { case 'V': std::cout << "rtems-trace-ld (RTEMS Trace Linker) " << rld::version () << ", RTEMS revision " << rld::rtems::version () << std::endl; ::exit (0); break; case 'v': rld::verbose_inc (); break; case 'w': #if HAVE_WARNINGS warnings = true; #endif break; case 'k': rld::process::set_keep_temporary_files (); break; case 'c': cc = optarg; break; case 'l': ld = optarg; break; case 'E': rld::cc::set_exec_prefix (optarg); break; case 'f': rld::cc::append_flags (optarg, rld::cc::ft_cflags); break; case 'r': rtems_path = optarg; break; case 'B': rtems_arch_bsp = optarg; break; case 'C': configuration = optarg; break; case 'P': if (!path.empty ()) path += ":"; path += optarg; break; case 'W': wrapper = optarg; break; case '?': usage (3); break; case 'h': usage (0); break; } } /* * Set the program name. */ rld::set_progname (argv[0]); argc -= optind; argv += optind; if (rld::verbose ()) { std::cout << "RTEMS Trace Linker " << rld::version () << std::endl; std::cout << " " << rld::get_cmdline () << std::endl; } /* * Load the arch/bsp value if provided. */ if (!rtems_arch_bsp.empty ()) { const std::string& prefix = rld::get_prefix (); if (rtems_path.empty () && prefix.empty ()) throw rld::error ("No RTEMS path provided with arch/bsp", "options"); if (!rtems_path.empty ()) rld::rtems::set_path (rtems_path); else rld::rtems::set_path (prefix); rld::rtems::set_arch_bsp (rtems_arch_bsp); } /** * Set the compiler and/or linker if provided. */ if (!cc.empty ()) rld::cc::set_cc (cc); if (!ld.empty ()) rld::cc::set_ld (ld); /* * Load the remaining command line arguments into the linker command line. */ while (argc--) { /* * Create this value because 'ld_cmd += ' ' + *argv++' fails on clang. */ std::string av = *argv++; ld_cmd += ' ' + av; } ld_cmd = rld::trim (ld_cmd); /* * If there are no object files there is nothing to link. */ if (ld_cmd.empty ()) throw rld::error ("no trace linker options", "options"); /* * Perform a trace link. */ try { linker.load_config (configuration, trace, path); rld::process::tempfile c (".c"); rld::process::tempfile o (".o"); if (!wrapper.empty ()) { c.override (wrapper); c.keep (); o.override (wrapper); o.keep (); } linker.generate_wrapper (c); linker.compile_wrapper (c, o); linker.link (o, ld_cmd); if (rld::verbose ()) linker.dump (std::cout); } catch (...) { throw; } } catch (rld::error re) { std::cerr << "error: " << re.where << ": " << re.what << std::endl; ec = 10; } catch (std::exception& e) { int status; char* realname; realname = abi::__cxa_demangle (e.what(), 0, 0, &status); std::cerr << "error: exception: " << realname << " ["; ::free (realname); const std::type_info &ti = typeid (e); realname = abi::__cxa_demangle (ti.name(), 0, 0, &status); std::cerr << realname << "] " << e.what () << std::endl; ::free (realname); ec = 11; } catch (...) { /* * Helps to know if this happens. */ std::cout << "error: unhandled exception" << std::endl; ec = 12; } return ec; }