/* * Copyright (c) 2014, 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 #ifndef HAVE_KILL #define kill(p,s) raise(s) #endif namespace rld { /** * RTEMS Trace Linker. */ namespace trace { /** * A container of arguments. */ typedef std::vector < std::string > function_args; /** * The return value. */ typedef std::string function_return; /** * 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); /** * Return the function's declaration. */ const std::string decl () 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. */ rld::strings headers; /**< Include statements. */ rld::strings defines; /**< Define statements. */ std::string map_sym_prefix; /**< Mapping symbol prefix. */ std::string arg_trace; /**< Code template to trace an argument. */ 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); /** * The the functions for the trace. */ void load_functions (rld::config::config& config, const rld::config::section& section); /** * The the traces for the tracer. */ void load_traces (rld::config::config& config, const rld::config::section& section); /** * Generate the wrapper object file. */ void generate (); /** * Generate the trace functions. */ void generate_traces (rld::process::tempfile& c); /** * Dump the wrapper. */ void dump (std::ostream& out) const; private: std::string name; /**< The name of the trace. */ std::string bsp; /**< The BSP we are linking to. */ rld::strings traces; /**< The functions to trace. */ 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& path, const std::string& trace); /** * Generate the C file. */ void generate_wrapper (); /** * Dump the linker. */ void dump (std::ostream& out) const; private: rld::config::config config; /**< User configuration. */ tracer tracer_; /**< The tracer */ }; /** * 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: " + record.name); if (si.size () == 1) throw rld::error ("no arguments", "signature: " + record.name); ret = si[0]; args.resize (si.size () - 1); std::copy (si.begin () + 1, si.end (), args.begin ()); } const std::string signature::decl () const { std::string ds = ret + ' ' + name + '('; 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); } 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) { const rld::config::section& sig_sec = config.get_section (*sli); 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. * # defines A list of define string that are single or double quoted. * # code-blocks A list of section names of code blocks. * # 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); parse (config, section, "code-blocks", "code", code, false); map_sym_prefix = section.get_record_item ("map-sym-prefix"); arg_trace = rld::dequote (section.get_record_item ("arg-trace")); ret_trace = rld::dequote (section.get_record_item ("ret-trace")); } void generator::dump (std::ostream& out) const { out << " Generator: " << 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 << " Mapping Symbol Prefix: " << map_sym_prefix << std::endl << " Arg Trace Code: " << arg_trace << std::endl << " Return Trace Code: " << ret_trace << std::endl << " Code blocks: " << std::endl; for (rld::strings::const_iterator ci = code.begin (); ci != code.end (); ++ci) { out << " > " << 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. * # bsp The architecture/bsp name of the BSP. * # options A list of options as per the long command line args. * # 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 * # bsp * # trace * # functions */ const rld::config::section& section = config.get_section (tname); config.includes (section); name = section.get_record_item ("name"); bsp = section.get_record_item ("bsp"); load_functions (config, section); load_traces (config, section); } 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_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) throw rld::error ("duplicate generators", "tracer: " + section.name); if (gens.size () == 0) { gen = config.get_section ("default-generator").get_record_item ("generator"); } else { gen = gens[0]; } generator_ = generator (config, gen); } void tracer::generate () { rld::process::tempfile c (".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 (" * Generator: " + generator_.name); c.write_line (" */"); c.write_lines (generator_.defines); c.write_lines (generator_.headers); c.write_line (""); c.write_lines (generator_.code); generate_traces (c); } catch (...) { c.close (); throw; } c.close (); } void tracer::generate_traces (rld::process::tempfile& c) { 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; } } } c.write_line (""); c.write_line ("/*"); c.write_line (" * Wrappers."); c.write_line (" */"); 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; c.write_line(""); c.write_line(sig.decl ()); c.write_line("{"); std::string l; /* * @todo Need to define as part of the function signature if ret * processing is required. */ if (sig.ret != "void") { c.write_line(" " + sig.ret + " ret;"); l = " ret ="; } l += " " + generator_.map_sym_prefix + sig.name + '('; 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 (sig.ret != "void") { c.write_line(" return ret;"); } c.write_line("}"); } } if (!found) throw rld::error ("not found", "trace function: " + trace); } } void tracer::dump (std::ostream& out) const { out << " Tracer: " << name << std::endl << " BSP: " << bsp << 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& path, const std::string& trace) { config.clear (); config.load (path); tracer_.load (config, trace); } void linker::generate_wrapper () { tracer_.generate (); } 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' }, { "exec-prefix", required_argument, NULL, 'E' }, { "march", required_argument, NULL, 'a' }, { "mcpu", required_argument, NULL, 'c' }, { "config", required_argument, NULL, 'C' }, { 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 << " -E prefix : the RTEMS tool prefix (also --exec-prefix)" << std::endl << " -c cflags : C compiler flags (also --cflags)" << std::endl << " -C ini : user configuration INI file (also --config)" << 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 ld_cmd; std::string configuration; std::string trace = "tracer"; bool exec_prefix_set = false; #if HAVE_WARNINGS bool warnings = false; #endif while (true) { int opt = ::getopt_long (argc, argv, "hvwkVE:c:C:", rld_opts, NULL); if (opt < 0) break; switch (opt) { case 'V': std::cout << "rtems-trace-ld (RTEMS Trace Linker) " << rld::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 'E': exec_prefix_set = true; rld::cc::exec_prefix = optarg; break; case 'c': rld::cc::cflags = optarg; break; case 'C': configuration = optarg; break; case '?': usage (3); break; case 'h': usage (0); break; } } argc -= optind; argv += optind; if (rld::verbose ()) std::cout << "RTEMS Trace Linker " << rld::version () << std::endl; /* * Load the remaining command line arguments into the linker command line. */ while (argc--) { if (ld_cmd.length () != 0) ld_cmd += " "; ld_cmd += *argv++; } /* * 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); linker.generate_wrapper (); 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; }