From 28884db485eba2b580d8da526e190f903b2e6174 Mon Sep 17 00:00:00 2001 From: Chris Johns Date: Mon, 4 Aug 2014 09:19:55 +1000 Subject: rtems-tld: Add trace configuration support. Extend the configuration support to provide the needed configuration required to generate the C stub support. --- rld-config.cpp | 40 ++++- rld-config.h | 95 +++++++++++ rld.h | 116 ++++++++++++-- rtems-tld.cpp | 485 +++++++++++++++++++++++++++++++++++++-------------------- rtems.ini | 20 +++ test-fsigs.ini | 3 - test-trace.ini | 35 ++++- 7 files changed, 609 insertions(+), 185 deletions(-) create mode 100644 rtems.ini delete mode 100644 test-fsigs.ini diff --git a/rld-config.cpp b/rld-config.cpp index b6b84c2..b197e5d 100644 --- a/rld-config.cpp +++ b/rld-config.cpp @@ -25,7 +25,6 @@ #include #include -#include #include @@ -127,6 +126,45 @@ namespace rld } } + + void + config::includes (const section& sec, bool must_exist) + { + bool have_includes = false; + + try + { + const rld::config::record& rec = sec.get_record ("include"); + + have_includes = true; + + /* + * Include records are a path which we can just load. + * + * @todo Add a search path. See 'rld::files' for details. We can default + * the search path to the install $prefix of this tool and we can + * then provide a default set of function signatures for RTEMS + * APIs. + */ + + for (rld::config::items::const_iterator ri = rec.items.begin (); + ri != rec.items.end (); + ++ri) + { + load ((*ri).text); + } + } + catch (rld::error re) + { + /* + * No include records, must be all inlined. If we have includes it must + * be another error so throw it. + */ + if (have_includes || (!have_includes && must_exist)) + throw; + } + } + const section& config::get_section (const std::string& name) const { diff --git a/rld-config.h b/rld-config.h index fe622e4..eb6d614 100644 --- a/rld-config.h +++ b/rld-config.h @@ -29,6 +29,8 @@ #include #include +#include + namespace rld { namespace config @@ -61,6 +63,13 @@ namespace rld { std::string name; //< Name of the record. items items; //< The record's items. + + /** + * Return true if there is only one item. + */ + bool single () const { + return items.size () == 1; + } }; /** @@ -115,6 +124,13 @@ namespace rld */ void load (const std::string& name); + /** + * Process any include records in the section named. If the section has + * any records named 'include' split the items and include the + * configuration files. + */ + void includes (const section& sec, bool must_exist = false); + /** * Get the section and throw an error if not found. */ @@ -130,6 +146,85 @@ namespace rld paths paths; /**< The path's of the loaded files. */ sections secs; /**< The sections loaded from configuration files */ }; + + /** + * Return the items from a record. + */ + template < typename T > + void parse_items (const rld::config::record& record, T& items) + { + items.clear (); + for (rld::config::items::const_iterator ii = record.items.begin (); + ii != record.items.end (); + ++ii) + { + rld::strings ss; + rld::split (ss, (*ii).text, ','); + std::copy (ss.begin (), ss.end (), std::back_inserter (items)); + } + } + + /** + * Return the items from a record in a section. Optionally raise an error + * if the record is not found and it is to be present. + */ + template < typename T > + void parse_items (const rld::config::section& section, + const std::string& name, + T& items, + bool present = false) + { + items.clear (); + const rld::config::record* rec = 0; + try + { + const rld::config::record& rr = section.get_record (name); + rec = &rr; + } + catch (rld::error re) + { + /* + * Ignore the error if it does not need to exist. + */ + if (present) + throw rld::error ("not found", "record: " + section.name + name); + } + + if (rec) + parse_items (*rec, items); + } + + /** + * Return the items from a record in a section in the + * configuration. Optionally raise an error if the section is not found and + * it is to be present. + */ + template < typename T > + void parse_items (const rld::config::config& config, + const std::string& section, + const std::string& record, + T& items, + bool present = false) + { + items.clear (); + const rld::config::section* sec = 0; + try + { + const rld::config::section& sr = config.get_section (section); + sec = &sr; + } + catch (rld::error re) + { + /* + * Ignore the error if it does not need to exist. + */ + if (present) + throw rld::error ("not found", "section: " + section); + } + + if (sec) + parse_items (*sec, record, items); + } } } diff --git a/rld.h b/rld.h index c6cd3ed..e1cfbf5 100644 --- a/rld.h +++ b/rld.h @@ -25,7 +25,11 @@ #if !defined (_RLD_H_) #define _RLD_H_ +#include +#include +#include #include +#include #include #include @@ -76,17 +80,6 @@ namespace rld namespace rld { - /** - * Convert a supported type to a string. - */ - template - std::string to_string (T t, std::ios_base & (*f)(std::ios_base&) = std::dec) - { - std::ostringstream oss; - oss << f << t; - return oss.str(); - } - /** * General error. */ @@ -110,6 +103,107 @@ namespace rld #define rld_error_at(_what) \ rld::error (_what, std::string (__FILE__) + ":" + to_string (__LINE__)) + /** + * Convert a supported type to a string. + */ + template + std::string to_string (T t, std::ios_base & (*f)(std::ios_base&) = std::dec) + { + std::ostringstream oss; + oss << f << t; + return oss.str(); + } + + /** + * A container of strings. + */ + typedef std::vector < std::string > strings; + + /** + * Trim from start. + */ + inline std::string& ltrim (std::string &s) + { + s.erase (s.begin (), + std::find_if (s.begin (), s.end (), + std::not1 (std::ptr_fun < int, int > (std::isspace)))); + return s; + } + + /** + * Trim from end. + */ + inline std::string& rtrim (std::string &s) + { + s.erase (std::find_if (s.rbegin (), s.rend (), + std::not1 (std::ptr_fun < int, int > (std::isspace))).base(), + s.end()); + return s; + } + + /** + * Trim from both ends. + */ + inline std::string& trim (std::string &s) + { + return ltrim (rtrim (s)); + } + + /** + * Split the string in a contain of strings based on the the + * delimiter. Optionally trim any white space or include empty string. + * + * @todo The split should optionally honour string quoting. + */ + inline strings& split (strings& se, + const std::string& s, + char delimiter, + bool strip_quotes = true, + bool strip_whitespace = true, + bool empty = false) + { + std::stringstream ss(s); + std::string e; + se.clear (); + while (std::getline (ss, e, delimiter)) + { + if (strip_whitespace) + trim (e); + if (strip_quotes) + { + if ((e.front () == '"') || (e.front () == '\'')) + { + if (e.front () != e.back ()) + throw rld::error ("invalid quoting", "string: " + s); + e = e.substr (1, e.length () - 1); + } + } + if (empty || !e.empty ()) + { + se.push_back (e); + } + } + return se; + } + + /** + * Join the strings together with the separator. + */ + inline std::string& join (const strings& ss, + const std::string& separator, + std::string& s) + { + for (strings::const_iterator ssi = ss.begin (); + ssi != ss.end (); + ++ssi) + { + s += *ssi; + if ((ssi != ss.begin ()) && (ssi != ss.end ())) + s += separator; + } + return s; + } + /** * Increment the verbose level. */ diff --git a/rtems-tld.cpp b/rtems-tld.cpp index edc6e55..4f17fba 100644 --- a/rtems-tld.cpp +++ b/rtems-tld.cpp @@ -52,226 +52,376 @@ namespace rld { - /** - * Trim from start. + * RTEMS Trace Linker. */ - inline std::string <rim (std::string &s) + namespace trace { - s.erase (s.begin (), - std::find_if (s.begin (), s.end (), - std::not1 (std::ptr_fun < int, int > (std::isspace)))); - return s; - } + /** + * A container of arguments. + */ + typedef std::vector < std::string > function_args; - /** - * Trim from end. - */ - inline std::string &rtrim (std::string &s) - { - s.erase (std::find_if (s.rbegin (), s.rend (), - std::not1 (std::ptr_fun < int, int > (std::isspace))).base(), - s.end()); - return s; - } + /** + * The return value. + */ + typedef std::string function_return; - /** - * Trim from both ends. - */ - inline std::string &trim (std::string &s) - { - return ltrim (rtrim (s)); - } -} + /** + * A function's signature. + */ + struct function_sig + { + std::string name; /**< The function's name. */ + function_args args; /**< The function's list of arguments. */ + function_return ret; /**< The fuctions return value. */ -/** - * RTEMS Trace Linker. - */ -namespace trace -{ - /** - * A container of arguments. - */ - typedef std::vector < std::string > function_args; + /** + * The default constructor. + */ + function_sig (); - /** - * The return value. - */ - typedef std::string function_return; + /** + * Construct the signature loading it from the configuration. + */ + function_sig (const rld::config::record& record); - /** - * A function's signature. - */ - struct function_sig - { - std::string name; /**< The function's name. */ - function_args args; /**< The function's list of arguments. */ - function_return ret; /**< The fuctions return value. */ - }; + /** + * Copy constructor. + */ + function_sig (const function_sig& orig); - /** - * A container of function signatures. - */ - typedef std::map < std::string, function_sig > function_sigs; + /** + * Return the function's declaration. + */ + const std::string decl () const; + }; - /** - * Trace Linker. - */ - class linker - { - public: - linker (); + /** + * A container of function signatures. + */ + typedef std::map < std::string, function_sig > function_sigs; /** - * Load the user's configuration. + * Wrappers hold the data used when wrapping the code. It knows how to wrap + * a specific trace function. Wrapping a function requires specific defines + * and header files. */ - void load_config (const std::string& path); + struct wrapper + { + std::string name; /**< The name of this wrapper. */ + rld::strings headers; /**< Include statements. */ + rld::strings defines; /**< Define statements. */ + function_sigs sigs; /**< The functions this wrapper wraps. */ + + /** + * Load the wrapper. + */ + wrapper (const std::string& name, + rld::config::config& config); + + /** + * Dump the wrapper. + */ + void dump (std::ostream& out) const; + }; /** - * Dump the linker status. + * A container of wrappers. The order is the order we wrap. */ - void dump (std::ostream& out); + typedef std::vector < wrapper > wrappers; - private: + /** + * Tracer. + */ + class tracer + { + public: + tracer (); - rld::config::config config; /**< User configuration. */ - function_sigs sigs; /**< Function signatures. */ - }; + /** + * Load the user's configuration. + */ + void load (rld::config::config& config, + const std::string& section); - linker::linker () - { - } + /** + * Dump the wrapper. + */ + void dump (std::ostream& out) const; - void - linker::load_config (const std::string& path) - { - config.clear (); - config.load (path); + private: - /* - * The configuration must contain a "trace" section. This is the top level - * configuration and must the following fields: - * - * # < add here > - * - * The 'trace" section may optionally contain a number of 'include' records - * and these configuration files are included. + std::string name; /**< The name of the trace. */ + std::string bsp; /**< The BSP we are linking to. */ + rld::strings trace; /**< The functions to trace. */ + wrappers wrappers; /**< Wrappers wrap trace functions. */ + }; + + /** + * Trace Linker. */ + class linker + { + public: + linker (); - const rld::config::section& tsec = config.get_section ("trace"); - bool have_includes = false; + /** + * Load the user's configuration. + */ + void load_config (const std::string& path, + const std::string& trace); - try + /** + * Generate the C file. + */ + void generate_c (); + + /** + * Dump the linker. + */ + void dump (std::ostream& out) const; + + private: + + rld::config::config config; /**< User configuration. */ + tracer tracer; /**< The tracer */ + }; + + function_sig::function_sig () { - const rld::config::record& irec = tsec.get_record ("include"); + } - have_includes = true; + function_sig::function_sig (const rld::config::record& record) + { + /* + * There can only be one function signature in the configuration. + */ + if (!record.single ()) + throw rld::error ("duplicate", "function signature: " + record.name); + + name = record.name; + + /* + * Function 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", "function signature: " + record.name); + if (si.size () == 1) + throw rld::error ("no arguments", "function signature: " + record.name); + + ret = si[0]; + args.resize (si.size () - 1); + std::copy (si.begin () + 1, si.end (), args.begin ()); + } + + function_sig::function_sig (const function_sig& orig) + : name (orig.name), + args (orig.args), + ret (orig.ret) + { + } + + const std::string + function_sig::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; + } + wrapper::wrapper (const std::string& name, + rld::config::config& config) + : name (name) + { /* - * Include records are a path which we can just load. + * A wrapper section optionally contain one or more records of: + * + * # header A list of include string that are single or double quoted. + * # define A list of define string that are single or double quoted. + * # signature A list of section names of function signatures. * - * @todo Add a search path. See 'rld::files' for details. We can default - * the search path to the install $prefix of this tool and we can - * then provide a default set of function signatures for RTEMS - * APIs. + * @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); - for (rld::config::items::const_iterator ii = irec.items.begin (); - ii != irec.items.end (); - ++ii) + rld::strings sig_list; + + rld::config::parse_items (section, "header", headers); + rld::config::parse_items (section, "define", defines); + rld::config::parse_items (section, "signature", sig_list); + + for (rld::strings::const_iterator sli = sig_list.begin (); + sli != sig_list.end (); + ++sli) { - config.load ((*ii).text); + const rld::config::section& sig_sec = config.get_section (*sli); + for (rld::config::records::const_iterator ri = sig_sec.recs.begin (); + ri != sig_sec.recs.end (); + ++ri) + { + function_sig func (*ri); + sigs[func.name] = func; + } } } - catch (rld::error re) + + void + wrapper::dump (std::ostream& out) const + { + out << " Wrapper: " << 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 << " Function Signatures: " << sigs.size () << std::endl; + for (function_sigs::const_iterator si = sigs.begin (); + si != sigs.end (); + ++si) + { + const function_sig& sig = (*si).second; + out << " " << sig.name << ": " << sig.decl () << ';' << std::endl; + } + } + + tracer::tracer () + { + } + + void + tracer::load (rld::config::config& config, + const std::string& section) { /* - * No include records, must be all inlined. If we have includes it must - * be another error so throw it. + * The configuration must contain a "trace" section. This is the top level + * configuration and must the following fields: + * + * # name The name of trace being linked. + * # trace The list of sections containing functions to trace. + * # wrapper The list of sections containing wrapping details. + * + * The following record are optional: + * + * # bdp The BSP the executable is for. Can be supplied on the command + * line. + * # include Include the INI file. + * + * The following will throw an error is the section or records are not + * found. */ - if (have_includes) - throw; - } + rld::strings ss; - /* - * Get the function signatures from the configuration and load them into - * the sig map. - */ + const rld::config::section& tsec = config.get_section (section); + const rld::config::record& nrec = tsec.get_record ("name"); + const rld::config::record& brec = tsec.get_record ("bsp"); + const rld::config::record& trec = tsec.get_record ("trace"); + const rld::config::record& wrec = tsec.get_record ("wrapper"); - const rld::config::section& fssec = config.get_section ("function-signatures"); + if (!nrec.single ()) + throw rld::error ("duplicate", "trace names"); + name = nrec.items[0].text; + + if (!brec.single ()) + throw rld::error ("duplicate", "trace bsp"); + bsp = brec.items[0].text; - for (rld::config::records::const_iterator ri = fssec.recs.begin (); - ri != fssec.recs.end (); - ++ri) - { /* - * There can only be one function signature in the configuration. + * Include any files. */ - if ((*ri).items.size () > 1) - throw rld::error ("duplicate", "function signature: " + (*ri).name); + config.includes (tsec); - function_sig sig; - sig.name = (*ri).name; + /* + * Load the wrappers. + */ + rld::strings wi; + rld::config::parse_items (wrec, wi); + for (rld::strings::const_iterator wsi = wi.begin (); + wsi != wi.end (); + ++wsi) + { + wrappers.push_back (wrapper (*wsi, config)); + } /* - * Function 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. + * Load the trace functions. */ - rld::config::items::const_iterator ii = (*ri).items.begin (); - std::stringstream ts((*ii).text); - std::string arg; - while (std::getline (ts, arg, ',')) + rld::strings ti; + rld::config::parse_items (trec, ti); + for (rld::strings::const_iterator tsi = ti.begin (); + tsi != ti.end (); + ++tsi) { - rld::trim (arg); - if (!arg.empty ()) - { - if (sig.ret.empty ()) - sig.ret = arg; - else - sig.args.push_back(arg); - } + rld::config::parse_items (config, *tsi, "trace", trace, true); } - if (sig.ret.empty ()) - throw rld::error ("no return value", "function signature: " + (*ri).name); + } - if (sig.args.empty ()) - throw rld::error ("no arguments", "function signature: " + (*ri).name); + void + tracer::dump (std::ostream& out) const + { + out << " Tracer: " << name << std::endl + << " BSP: " << bsp << std::endl; + for (wrappers::const_iterator wi = wrappers.begin (); + wi != wrappers.end (); + ++wi) + { + (*wi).dump (out); + } + } - sigs[sig.name] = sig; + linker::linker () + { } - } - void - linker::dump (std::ostream& out) - { - 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) + void + linker::load_config (const std::string& path, + const std::string& trace) { - out << " " << (*pi) << std::endl; + config.clear (); + config.load (path); + tracer.load (config, trace); } - out << std::endl - << "Function Signatures: " << sigs.size () << std::endl; - for (function_sigs::const_iterator si = sigs.begin (); - si != sigs.end (); - ++si) + void + linker::dump (std::ostream& out) const { - const function_sig& sig = (*si).second; - out << " " << sig.name << ": " << sig.ret << ' ' << sig.name << '('; - for (function_args::const_iterator fai = sig.args.begin (); - fai != sig.args.end (); - ++fai) + const rld::config::paths& cpaths = config.get_paths (); + out << "RTEMS Trace Linker" << std::endl + << " Configuration Files: " << cpaths.size () << std::endl; + for (rld::config::paths::const_iterator pi = cpaths.begin (); + pi != cpaths.end (); + ++pi) { - if (fai != sig.args.begin ()) - out << ", "; - out << (*fai); + out << " " << (*pi) << std::endl; } - out << ");" << std::endl; + + tracer.dump (out); } } } @@ -353,12 +503,13 @@ main (int argc, char* argv[]) try { - trace::linker linker; - std::string ld_cmd; - std::string configuration; - bool exec_prefix_set = false; + 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; + bool warnings = false; #endif while (true) @@ -439,7 +590,7 @@ main (int argc, char* argv[]) */ try { - linker.load_config (configuration); + linker.load_config (configuration, trace); linker.dump (std::cout); } catch (...) diff --git a/rtems.ini b/rtems.ini new file mode 100644 index 0000000..cd59f72 --- /dev/null +++ b/rtems.ini @@ -0,0 +1,20 @@ +; +; RTEMS API Trace Configurations +; +[rtems-api] +header = rtems-api-headers +define = rtems-api-defines +signature = rtems-api-signatures + +[rtems-api-headers] +header = "#include " + +[rtems-api-defines] +; Currently empty + +[rtems-api-task] +trace = rtems_task_create + +[rtems-api-signatures] +rtems_task_create = rtems_status_code, rtems_name, rtems_task_priority, size_t, rtems_mode, rtems_attribute, rtems_id* + diff --git a/test-fsigs.ini b/test-fsigs.ini deleted file mode 100644 index a0d1e22..0000000 --- a/test-fsigs.ini +++ /dev/null @@ -1,3 +0,0 @@ -[function-signatures] -rtems_task_create = rtems_status_code, rtems_name, rtems_task_priority, size_t, rtems_mode, rtems_attribute, rtems_id* - diff --git a/test-trace.ini b/test-trace.ini index 049e8e8..2901952 100644 --- a/test-trace.ini +++ b/test-trace.ini @@ -3,12 +3,41 @@ ; ; We must provide a top level trace section. ; -[trace] +[tracer] ; ; Name of the trace. ; name = RTEMS Trace Linker Test ; -; Include the function signatures. +; The BSP. ; -include = test-fsigs.ini +bsp = sis +; +; Functions to trace. +; +trace = test-trace, test-trace-funcs, rtems-api-task +; +; Define the wrapper. +; +wrapper = test-trace, rtems-api +; +; Include RTEMS Trace support. +; +include = rtems.ini + +; +; User application trace example. +; +[test-trace-funcs] +trace = test_trace_1, test_trace_2 + +[test-trace] +trace = test_trace_3 +header = '#include "test-trace.h"' +define = "#define TEST_TRACE 1" +signature = test-signatures + +[test-signatures] +test_trace_1 = void, int +test_trace_2 = test_type_2, test_type_1 +test_trace_3 = float, float* -- cgit v1.2.3