| /** |
| * @file xml_utils.cpp |
| * utility routines for generating XML |
| * |
| * @remark Copyright 2006 OProfile authors |
| * @remark Read the file COPYING |
| * |
| * @author Dave Nomura |
| */ |
| |
| #include <iostream> |
| #include <sstream> |
| |
| #include "xml_utils.h" |
| #include "format_output.h" |
| #include "arrange_profiles.h" |
| #include "op_bfd.h" |
| #include "cverb.h" |
| |
| using namespace std; |
| |
| bool want_xml = false; |
| |
| size_t nr_classes = 0; |
| size_t nr_cpus = 0; |
| size_t nr_events = 0; |
| sym_iterator symbols_begin; |
| sym_iterator symbols_end; |
| // handle on xml_formatter object |
| format_output::xml_formatter * xml_out; |
| xml_utils * xml_support; |
| size_t xml_utils::events_index = 0; |
| bool xml_utils::has_nonzero_masks = false; |
| ostringstream xml_options; |
| |
| |
| |
| namespace { |
| |
| bool has_separated_cpu_info() |
| { |
| return classes.v[0].ptemplate.cpu != "all"; |
| } |
| |
| |
| string get_event_num(size_t pclass) |
| { |
| return classes.v[pclass].ptemplate.event; |
| } |
| |
| |
| size_t get_next_event_num_pclass(size_t start) |
| { |
| string cur_event = get_event_num(start); |
| size_t i; |
| for (i = start; |
| i < nr_classes && get_event_num(i) == cur_event; |
| ++i) ; |
| return i; |
| } |
| |
| |
| void dump_symbol(string const & prefix, sym_iterator it, bool want_nl = true) |
| { |
| if (it == symbols_end) |
| cverb << vxml << prefix << "END"; |
| else |
| cverb << vxml << prefix << symbol_names.name((*it)->name); |
| if (want_nl) |
| cverb << vxml << endl; |
| } |
| |
| |
| void dump_symbols(string const & prefix, sym_iterator b, sym_iterator e) |
| { |
| if (b == (sym_iterator)0) |
| return; |
| |
| for (sym_iterator it = b; it != e; ++it) |
| dump_symbol(prefix, it, true); |
| } |
| |
| |
| |
| void dump_classes() |
| { |
| cverb << vxml << "<!-- classes dump" << endl; |
| cverb << vxml << classes.event; |
| cverb << vxml << "classes.size= " << classes.v.size() << endl; |
| for (size_t i = 0; i < classes.v.size(); ++i) { |
| cverb << vxml << "--- class " << i << ":" << classes.v[i].name << " ---" << endl; |
| cverb << vxml << classes.v[i].ptemplate; |
| } |
| cverb << vxml << "-->" << endl; |
| } |
| |
| |
| bool has_separated_thread_info() |
| { |
| return classes.v[0].ptemplate.tid != "all"; |
| } |
| |
| |
| string get_cpu_num(size_t pclass) |
| { |
| return classes.v[pclass].ptemplate.cpu; |
| } |
| |
| |
| }; // anonymous namespace |
| |
| xml_utils::xml_utils(format_output::xml_formatter * xo, |
| symbol_collection const & s, size_t nc, |
| extra_images const & extra) |
| : |
| has_subclasses(false), |
| bytes_index(0), |
| extra_found_images(extra) |
| { |
| xml_out = xo; |
| nr_classes = nc; |
| symbols_begin = s.begin(); |
| symbols_end = s.end(); |
| multiple_events = get_next_event_num_pclass(0) != nr_classes; |
| |
| if (has_separated_cpu_info()) { |
| size_t cpus = 0; |
| // count number of cpus |
| for (size_t p = 0; p < nr_classes; ++p) { |
| size_t cpu = atoi(classes.v[p].ptemplate.cpu.c_str()); |
| if (cpu > cpus) cpus = cpu; |
| } |
| // cpus names start with 0 |
| nr_cpus = cpus + 1; |
| } |
| } |
| |
| |
| string xml_utils::get_timer_setup(size_t count) |
| { |
| return open_element(TIMER_SETUP, true) + |
| init_attr(RTC_INTERRUPTS, count) + close_element(); |
| } |
| |
| |
| string xml_utils::get_event_setup(string event, size_t count, |
| string unit_mask) |
| { |
| ostringstream str; |
| |
| str << open_element(EVENT_SETUP, true); |
| str << init_attr(TABLE_ID, events_index++); |
| str << init_attr(EVENT_NAME, event); |
| if (unit_mask.size() != 0) str << init_attr(UNIT_MASK, unit_mask); |
| str << init_attr(SETUP_COUNT, (size_t)count) + close_element(); |
| return str.str(); |
| } |
| |
| |
| string xml_utils::get_profile_header(string cpu_name, double const speed) |
| { |
| ostringstream str; |
| string cpu_type; |
| string processor; |
| string::size_type slash_pos = cpu_name.find("/"); |
| |
| if (slash_pos == string::npos) { |
| cpu_type = cpu_name; |
| processor = ""; |
| } else { |
| cpu_type = cpu_name.substr(0, slash_pos); |
| processor = cpu_name.substr(slash_pos+1); |
| } |
| |
| str << init_attr(CPU_NAME, cpu_type) << endl; |
| if (processor.size() > 0) |
| str << init_attr(PROCESSOR, string(processor)) << endl; |
| if (nr_cpus > 1) str << init_attr(SEPARATED_CPUS, nr_cpus) << endl; |
| str << init_attr(MHZ, speed) << endl; |
| |
| return str.str(); |
| } |
| |
| |
| void xml_utils::set_nr_cpus(size_t cpus) |
| { |
| nr_cpus = cpus; |
| } |
| |
| void xml_utils::set_nr_events(size_t events) |
| { |
| nr_events = events; |
| } |
| |
| void xml_utils::set_has_nonzero_masks() |
| { |
| has_nonzero_masks = true; |
| } |
| |
| |
| void xml_utils::add_option(tag_t tag, string const & value) |
| { |
| xml_options << init_attr(tag, value); |
| } |
| |
| |
| void xml_utils::add_option(tag_t tag, list<string> const & value) |
| { |
| list<string>::const_iterator begin = value.begin(); |
| list<string>::const_iterator end = value.end(); |
| list<string>::const_iterator cit = begin; |
| ostringstream str; |
| |
| for (; cit != end; ++cit) { |
| if (cit != begin) |
| str << ","; |
| str << *cit; |
| } |
| xml_options << init_attr(tag, str.str()); |
| } |
| |
| |
| void xml_utils::add_option(tag_t tag, vector<string> const & value) |
| { |
| vector<string>::const_iterator begin = value.begin(); |
| vector<string>::const_iterator end = value.end(); |
| vector<string>::const_iterator cit = begin; |
| ostringstream str; |
| |
| for (; cit != end; ++cit) { |
| if (cit != begin) |
| str << ","; |
| str << *cit; |
| } |
| xml_options << init_attr(tag, str.str()); |
| } |
| |
| |
| void xml_utils::add_option(tag_t tag, bool value) |
| { |
| xml_options << init_attr(tag, (value ? "true" : "false")); |
| } |
| |
| |
| void xml_utils::output_xml_header(string const & command_options, |
| string const & cpu_info, string const & events) |
| { |
| // the integer portion indicates the schema version and should change |
| // both here and in the schema file when major changes are made to |
| // the schema. changes to opreport, or minor changes to the schema |
| // can be indicated by changes to the fraction part. |
| string const schema_version = "3.0"; |
| |
| // This is the XML version, not schema version. |
| string const xml_header = "<?xml version=\"1.0\" ?>"; |
| |
| cout << xml_header << endl; |
| cout << open_element(PROFILE, true); |
| cout << init_attr(SCHEMA_VERSION, schema_version); |
| |
| cout << cpu_info; |
| cout << init_attr(TITLE, "opreport " + command_options); |
| cout << close_element(NONE, true); |
| |
| cout << open_element(OPTIONS, true) << xml_options.str(); |
| cout << close_element(); |
| |
| cout << open_element(SETUP) << events; |
| cout << close_element(SETUP) << endl; |
| } |
| |
| class subclass_info_t { |
| public: |
| string unitmask; |
| string subclass_name; |
| }; |
| |
| typedef growable_vector<subclass_info_t> subclass_array_t; |
| typedef growable_vector<subclass_array_t> event_subclass_t; |
| typedef growable_vector<event_subclass_t> cpu_subclass_t; |
| |
| void xml_utils::build_subclasses(ostream & out) |
| { |
| size_t subclasses = 0; |
| string subclass_name; |
| // when --separate=cpu we will have an event_subclass array for each cpu |
| cpu_subclass_t cpu_subclasses; |
| |
| event_subclass_t event_subclasses; |
| |
| if (nr_cpus <= 1 && nr_events <= 1 && !has_nonzero_masks) |
| return; |
| |
| out << open_element(CLASSES); |
| for (size_t i = 0; i < classes.v.size(); ++i) { |
| profile_class & pclass = classes.v[i]; |
| size_t event = atoi(pclass.ptemplate.event.c_str()); |
| |
| subclass_array_t * sc_ptr; |
| |
| // select the right subclass array |
| if (nr_cpus == 1) { |
| sc_ptr = &event_subclasses[event]; |
| } else { |
| size_t cpu = atoi(pclass.ptemplate.cpu.c_str()); |
| sc_ptr = &cpu_subclasses[cpu][event]; |
| } |
| |
| // search for an existing unitmask |
| subclass_name = ""; |
| for (size_t j = 0; j < sc_ptr->size(); ++j) { |
| if ((*sc_ptr)[j].unitmask == pclass.ptemplate.unitmask) { |
| subclass_name = (*sc_ptr)[j].subclass_name; |
| break; |
| } |
| } |
| |
| if (subclass_name.size() == 0) { |
| ostringstream str; |
| size_t new_index = sc_ptr->size(); |
| |
| // no match found, create a new entry |
| str << "c" << subclasses++; |
| subclass_name = str.str(); |
| (*sc_ptr)[new_index].unitmask = pclass.ptemplate.unitmask; |
| (*sc_ptr)[new_index].subclass_name = subclass_name; |
| out << open_element(CLASS, true); |
| out << init_attr(NAME, subclass_name); |
| if (nr_cpus > 1) |
| out << init_attr(CPU_NUM, pclass.ptemplate.cpu); |
| if (nr_events > 1) |
| out << init_attr(EVENT_NUM, event); |
| if (has_nonzero_masks) |
| out << init_attr(EVENT_MASK, pclass.ptemplate.unitmask); |
| out << close_element(); |
| } |
| |
| pclass.name = subclass_name; |
| } |
| out << close_element(CLASSES); |
| has_subclasses = true; |
| } |
| |
| |
| string |
| get_counts_string(count_array_t const & counts, size_t begin, size_t end) |
| { |
| ostringstream str; |
| bool got_count = false; |
| |
| // if no cpu separation then return a simple count, omit zero counts |
| if (nr_cpus == 1) { |
| size_t count = counts[begin]; |
| if (count == 0) |
| return ""; |
| str << count; |
| return str.str(); |
| } |
| |
| for (size_t p = begin; p != end; ++p) { |
| size_t count = counts[p]; |
| if (p != begin) str << ","; |
| if (count != 0) { |
| got_count = true; |
| str << count; |
| } |
| } |
| return got_count ? str.str() : ""; |
| } |
| |
| |
| void |
| xml_utils::output_symbol_bytes(ostream & out, symbol_entry const * symb, |
| size_t sym_id, op_bfd const & abfd) |
| { |
| size_t size = symb->size; |
| scoped_array<unsigned char> contents(new unsigned char[size]); |
| if (abfd.get_symbol_contents(symb->sym_index, contents.get())) { |
| string const name = symbol_names.name(symb->name); |
| out << open_element(BYTES, true) << init_attr(TABLE_ID, sym_id); |
| out << close_element(NONE, true); |
| for (size_t i = 0; i < size; ++i) { |
| char hex_map[] = "0123456789ABCDEF"; |
| char hex[2]; |
| hex[0] = hex_map[(contents[i] >> 4) & 0xf]; |
| hex[1] = hex_map[contents[i] & 0xf]; |
| out << hex[0] << hex[1]; |
| } |
| out << close_element(BYTES); |
| } |
| } |
| |
| |
| bool |
| xml_utils::output_summary_data(ostream & out, count_array_t const & summary, size_t pclass) |
| { |
| size_t const count = summary[pclass]; |
| |
| if (count == 0) |
| return false; |
| |
| out << open_element(COUNT, has_subclasses); |
| if (has_subclasses) { |
| out << init_attr(CLASS, classes.v[pclass].name); |
| out << close_element(NONE, true); |
| } |
| out << count; |
| out << close_element(COUNT); |
| return true; |
| } |
| |
| class module_info { |
| public: |
| module_info() |
| { lo = hi = 0; name = ""; begin = end = (sym_iterator)0;} |
| void dump(); |
| void build_module(string const & n, sym_iterator it, |
| size_t l, size_t h); |
| string get_name() { return name; } |
| void set_lo(size_t l) { lo = l; } |
| void set_hi(size_t h) { hi = h; } |
| count_array_t const & get_summary() { return summary; } |
| void set_begin(sym_iterator b); |
| void set_end(sym_iterator e); |
| void add_to_summary(count_array_t const & counts); |
| void output(ostream & out); |
| bool is_closed(string const & n); |
| protected: |
| void output_summary(ostream & out); |
| void output_symbols(ostream & out, bool is_module); |
| |
| string name; |
| sym_iterator begin; |
| sym_iterator end; |
| |
| // summary sample data |
| count_array_t summary; |
| |
| // range of profile classes approprate for this module |
| size_t lo; |
| size_t hi; |
| }; |
| |
| class thread_info : public module_info { |
| public: |
| thread_info() { nr_modules = 0; } |
| |
| void build_thread(string const & tid, size_t l, size_t h); |
| bool add_modules(string const & module, sym_iterator it); |
| void add_module_symbol(string const & n, sym_iterator it); |
| void summarize(); |
| void set_end(sym_iterator end); |
| string const get_tid() { return thread_id; } |
| void output(ostream & out); |
| void dump(); |
| private: |
| // indices into the classes array applicable to this process |
| size_t nr_modules; |
| string thread_id; |
| growable_vector<module_info> my_modules; |
| }; |
| |
| class process_info : public module_info { |
| public: |
| process_info() { nr_threads = 0; } |
| void build_process(string const & pid, size_t l, size_t h); |
| void add_thread(string const & tid, size_t l, size_t h); |
| void add_modules(string const & module, |
| string const & app_name, sym_iterator it); |
| void summarize(); |
| void set_end(sym_iterator end); |
| void output(ostream & out); |
| void dump(); |
| private: |
| size_t nr_threads; |
| string process_id; |
| growable_vector<thread_info> my_threads; |
| |
| }; |
| class process_root_info { |
| public: |
| process_root_info() { nr_processes = 0; } |
| process_info * add_process(string const & pid, size_t lo, size_t hi); |
| void add_modules(string const & module, string const & app_name, |
| sym_iterator it); |
| void summarize(); |
| void summarize_processes(extra_images const & extra_found_images); |
| void set_process_end(); |
| void output_process_symbols(ostream & out); |
| void dump_processes(); |
| private: |
| size_t nr_processes; |
| |
| growable_vector<process_info> processes; |
| }; |
| |
| class binary_info : public module_info { |
| public: |
| binary_info() { nr_modules = 0; } |
| void output(ostream & out); |
| binary_info * build_binary(string const & n); |
| void add_module_symbol(string const & module, string const & app, |
| sym_iterator it); |
| void close_binary(sym_iterator it); |
| void dump(); |
| private: |
| size_t nr_modules; |
| |
| growable_vector<module_info> my_modules; |
| }; |
| |
| |
| class binary_root_info { |
| public: |
| binary_root_info() { nr_binaries = 0; } |
| binary_info * add_binary(string const & n, sym_iterator it); |
| void summarize_binaries(extra_images const & extra_found_images); |
| void output_binary_symbols(ostream & out); |
| void dump_binaries(); |
| private: |
| size_t nr_binaries; |
| |
| growable_vector<binary_info> binaries; |
| }; |
| |
| static process_root_info processes_root; |
| static binary_root_info binaries_root; |
| |
| |
| void module_info:: |
| build_module(string const & n, sym_iterator it, size_t l, size_t h) |
| { |
| name = n; |
| begin = it; |
| lo = l; |
| hi = h; |
| } |
| |
| |
| void module_info::add_to_summary(count_array_t const & counts) |
| { |
| for (size_t pclass = lo ; pclass <= hi; ++pclass) |
| summary[pclass] += counts[pclass]; |
| } |
| |
| |
| void module_info::set_begin(sym_iterator b) |
| { |
| if (begin == (sym_iterator)0) |
| begin = b; |
| } |
| |
| |
| void module_info::set_end(sym_iterator e) |
| { |
| if (end == (sym_iterator)0) |
| end = e; |
| } |
| |
| |
| bool module_info::is_closed(string const & n) |
| { |
| return (name == n) && end != (sym_iterator)0; |
| } |
| |
| |
| void module_info::dump() |
| { |
| cverb << vxml << " module:class(" << lo << "," << hi << ")="; |
| cverb << vxml << name << endl; |
| dump_symbols(" ", begin, end); |
| } |
| |
| |
| void module_info::output(ostream & out) |
| { |
| out << open_element(MODULE, true); |
| out << init_attr(NAME, name) << close_element(NONE, true); |
| output_summary(out); |
| output_symbols(out, true); |
| out << close_element(MODULE); |
| } |
| |
| |
| void module_info::output_summary(ostream & out) |
| { |
| for (size_t p = lo; p <= hi; ++p) |
| (void)xml_support->output_summary_data(out, summary, p); |
| } |
| |
| |
| void module_info::output_symbols(ostream & out, bool is_module) |
| { |
| if (begin == (sym_iterator)0) |
| return; |
| |
| for (sym_iterator it = begin; it != end; ++it) |
| xml_out->output_symbol(out, *it, lo, hi, is_module); |
| } |
| |
| |
| void binary_info::close_binary(sym_iterator it) |
| { |
| set_end(it); |
| if (nr_modules > 0) { |
| module_info & m = my_modules[nr_modules-1]; |
| m.set_end(it); |
| } |
| } |
| |
| |
| void binary_info::dump() |
| { |
| cverb << vxml << "app_name=" << name << endl; |
| if (begin != (sym_iterator)0) |
| dump_symbols(" ", begin, end); |
| |
| for (size_t i = 0; i < nr_modules; ++i) |
| my_modules[i].dump(); |
| } |
| |
| |
| void binary_info:: |
| add_module_symbol(string const & module, string const & app, |
| sym_iterator it) |
| { |
| size_t m = nr_modules; |
| |
| if (module == app) { |
| // set begin symbol for binary if not set |
| set_begin(it); |
| |
| if (m > 0) { |
| // close out current module |
| module_info & mod = my_modules[m-1]; |
| mod.set_end(it); |
| } |
| |
| // add symbol count to binary count |
| add_to_summary((*it)->sample.counts); |
| return; |
| } |
| |
| string current_module_name = (m == 0 ? "" : my_modules[m-1].get_name()); |
| if (module != current_module_name) { |
| // we have a module distinct from it's binary: --separate=lib |
| // and this is the first symbol for this module |
| if (m != 0) { |
| // close out current module |
| module_info & mod = my_modules[m-1]; |
| mod.set_end(it); |
| add_to_summary(mod.get_summary()); |
| } |
| |
| // mark end of enclosing binary symbols if there have been any |
| // NOTE: it is possible for the binary's symbols to follow its |
| // module symbols |
| if (begin != (sym_iterator)0 && end == (sym_iterator)0) |
| set_end(it); |
| |
| // build the new module |
| nr_modules++; |
| my_modules[m].build_module(module, it, 0, nr_classes-1); |
| } |
| |
| // propagate this symbols counts to the module |
| my_modules[nr_modules-1].add_to_summary((*it)->sample.counts); |
| } |
| |
| |
| void binary_root_info:: |
| summarize_binaries(extra_images const & extra_found_images) |
| { |
| binary_info * current_binary = 0; |
| string current_binary_name = ""; |
| |
| for (sym_iterator it = symbols_begin ; it != symbols_end; ++it) { |
| string binary = get_image_name((*it)->app_name, |
| image_name_storage::int_filename, extra_found_images); |
| string module = get_image_name((*it)->image_name, |
| image_name_storage::int_filename, extra_found_images); |
| |
| if (binary != current_binary_name) { |
| current_binary = binaries_root.add_binary(binary, it); |
| current_binary_name = binary; |
| } |
| |
| current_binary->add_module_symbol(module, binary, it); |
| } |
| |
| // close out last binary and module |
| current_binary->close_binary(symbols_end); |
| } |
| |
| |
| process_info * |
| process_root_info::add_process(string const & pid, size_t lo, size_t hi) |
| { |
| processes[nr_processes].build_process(pid, lo, hi); |
| return &processes[nr_processes++]; |
| } |
| |
| |
| void process_root_info:: |
| add_modules(string const & module, string const & app_name, |
| sym_iterator it) |
| { |
| for (size_t p = 0; p < nr_processes; ++p) |
| processes[p].add_modules(module, app_name, it); |
| } |
| |
| |
| |
| void process_root_info::summarize() |
| { |
| for (size_t p = 0; p < nr_processes; ++p) |
| processes[p].summarize(); |
| } |
| |
| |
| void process_root_info:: |
| summarize_processes(extra_images const & extra_found_images) |
| { |
| // add modules to the appropriate threads in the process hierarchy |
| for (sym_iterator it = symbols_begin ; it != symbols_end; ++it) { |
| string binary = get_image_name((*it)->app_name, |
| image_name_storage::int_filename, extra_found_images); |
| string module = get_image_name((*it)->image_name, |
| image_name_storage::int_filename, extra_found_images); |
| |
| processes_root.add_modules(module, binary, it); |
| } |
| |
| // set end symbol boundary for all modules in all threads |
| processes_root.set_process_end(); |
| |
| // propagate summaries to process/thread |
| processes_root.summarize(); |
| } |
| |
| |
| void process_root_info::set_process_end() |
| { |
| for (size_t p = 0; p < nr_processes; ++p) |
| processes[p].set_end(symbols_end); |
| } |
| |
| void process_root_info::output_process_symbols(ostream & out) |
| { |
| for (size_t p = 0; p < nr_processes; ++p) |
| processes[p].output(out); |
| } |
| |
| |
| void process_root_info::dump_processes() |
| { |
| cverb << vxml << "<!-- processes_dump:" << endl; |
| for (size_t p = 0; p < nr_processes; ++p) |
| processes[p].dump(); |
| cverb << vxml << "end processes_dump -->" << endl; |
| } |
| |
| binary_info * |
| binary_info::build_binary(string const & n) |
| { |
| name = n; |
| lo = 0; |
| hi = nr_classes-1; |
| return this; |
| } |
| |
| |
| void binary_info::output(ostream & out) |
| { |
| out << open_element(BINARY, true); |
| out << init_attr(NAME, name) << close_element(NONE, true); |
| |
| output_summary(out); |
| output_symbols(out, false); |
| for (size_t a = 0; a < nr_modules; ++a) |
| my_modules[a].output(out); |
| |
| out << close_element(BINARY); |
| } |
| |
| |
| binary_info * |
| binary_root_info::add_binary(string const & n, sym_iterator it) |
| { |
| size_t a = nr_binaries++; |
| |
| // close out previous binary and module |
| if (a > 0) binaries[a-1].close_binary(it); |
| return binaries[a].build_binary(n); |
| } |
| |
| |
| void binary_root_info::output_binary_symbols(ostream & out) |
| { |
| for (size_t a = 0; a < nr_binaries; ++a) |
| binaries[a].output(out); |
| } |
| |
| |
| void binary_root_info::dump_binaries() |
| { |
| cverb << vxml << "<!-- binaries_dump:" << endl; |
| for (size_t p = 0; p < nr_binaries; ++p) |
| binaries[p].dump(); |
| cverb << vxml << "end processes_dump -->" << endl; |
| } |
| |
| |
| void process_info::build_process(string const & pid, size_t l, size_t h) |
| { |
| process_id = pid; |
| lo = l; |
| hi = h; |
| } |
| |
| |
| void process_info::add_thread(string const & tid, size_t l, size_t h) |
| { |
| my_threads[nr_threads++].build_thread(tid, l, h); |
| } |
| |
| |
| void process_info::add_modules(string const & module, |
| string const & app_name, sym_iterator it) |
| { |
| bool added = false; |
| for (size_t t = 0; t < nr_threads; ++t) |
| added |= my_threads[t].add_modules(module, it); |
| if (added && name.size() == 0) name = app_name; |
| } |
| |
| |
| void process_info::summarize() |
| { |
| for (size_t t = 0; t < nr_threads; ++t) { |
| thread_info & thr = my_threads[t]; |
| thr.summarize(); |
| add_to_summary(thr.get_summary()); |
| } |
| } |
| |
| |
| void thread_info::build_thread(string const & tid, size_t l, size_t h) |
| { |
| thread_id = tid; |
| lo = l; |
| hi = h; |
| } |
| |
| |
| void thread_info::summarize() |
| { |
| for (size_t m = 0; m < nr_modules; ++m) |
| add_to_summary(my_modules[m].get_summary()); |
| } |
| |
| |
| void thread_info::set_end(sym_iterator end) |
| { |
| for (size_t m = 0; m < nr_modules; ++m) |
| my_modules[m].set_end(end); |
| } |
| |
| |
| void thread_info::add_module_symbol(string const & n, sym_iterator it) |
| { |
| module_info & m = my_modules[nr_modules++]; |
| m.build_module(n, it, lo, hi); |
| m.add_to_summary((*it)->sample.counts); |
| } |
| |
| void thread_info::output(ostream & out) |
| { |
| ostringstream thread_summary; |
| ostringstream modules_output; |
| |
| output_summary(thread_summary); |
| |
| for (size_t m = 0; m < nr_modules; ++m) |
| my_modules[m].output(modules_output); |
| |
| // ignore threads with no sample data |
| if (modules_output.str().size() == 0 && thread_summary.str().size() == 0) |
| return; |
| |
| out << open_element(THREAD, true); |
| out << init_attr(THREAD_ID, thread_id) << close_element(NONE, true); |
| out << thread_summary.str(); |
| out << modules_output.str(); |
| out << close_element(THREAD); |
| } |
| |
| |
| bool thread_info::add_modules(string const & module, sym_iterator it) |
| { |
| string old_name = |
| (nr_modules == 0 ? "" : my_modules[nr_modules-1].get_name()); |
| if (nr_modules > 0 && old_name != module) { |
| module_info & m = my_modules[nr_modules-1]; |
| // close out previous module if it hasn't already been closed out |
| if (!m.is_closed(old_name)) |
| m.set_end(it); |
| } |
| |
| // add a new module for this symbol if it has a non-zero count |
| if (nr_modules == 0 || module != old_name) { |
| if (has_sample_counts((*it)->sample.counts, lo, hi)) { |
| add_module_symbol(module, it); |
| return true; |
| } |
| } else { |
| // propagate symbols count to module |
| my_modules[nr_modules-1].add_to_summary((*it)->sample.counts); |
| } |
| return false; |
| } |
| |
| |
| void thread_info::dump() |
| { |
| cverb << vxml << "tid=" << thread_id << endl; |
| for (size_t i = 0; i < nr_modules; ++i) |
| my_modules[i].dump(); |
| } |
| |
| |
| void process_info::set_end(sym_iterator end) |
| { |
| for (size_t t = 0; t < nr_threads; ++t) |
| my_threads[t].set_end(end); |
| } |
| |
| |
| void process_info::output(ostream & out) |
| { |
| ostringstream process_summary; |
| ostringstream thread_output; |
| |
| output_summary(process_summary); |
| |
| for (size_t t = 0; t < nr_threads; ++t) |
| my_threads[t].output(thread_output); |
| |
| // ignore processes with no sample data |
| if (thread_output.str().size() == 0 && process_summary.str().size() == 0) |
| return; |
| |
| out << open_element(PROCESS, true); |
| out << init_attr(PROC_ID, process_id); |
| out << init_attr(NAME, name) << close_element(NONE, true); |
| out << process_summary.str(); |
| out << thread_output.str(); |
| out << close_element(PROCESS); |
| } |
| |
| |
| void process_info::dump() |
| { |
| cverb << vxml << "pid=" << process_id << " app=" << name << endl; |
| for (size_t i = 0; i < nr_threads; ++i) |
| my_threads[i].dump(); |
| } |
| |
| size_t get_next_tgid_pclass(size_t start) |
| { |
| string cur_tgid = classes.v[start].ptemplate.tgid; |
| size_t i = start; |
| for (i = start; |
| i < nr_classes && classes.v[i].ptemplate.tgid == cur_tgid; |
| ++i) ; |
| return i; |
| } |
| |
| |
| size_t get_next_tid_pclass(size_t start) |
| { |
| string cur_tid = classes.v[start].ptemplate.tid; |
| size_t i; |
| for (i = start; |
| i < nr_classes && classes.v[i].ptemplate.tid == cur_tid; |
| ++i) ; |
| return i; |
| } |
| |
| |
| // build the process/thread/module hierarchy that will allow us later |
| // to collect the summary sample data at each level and then |
| // traverse the hierarchy to intersperse the summary data for the |
| // symbols |
| void build_process_tree() |
| { |
| size_t tgid = 0; |
| size_t tid = 0; |
| |
| // build the structure representing the process/thread/module hierarchy |
| // for holding the summary data associated with each level and to be |
| // traversed when outputting the body of the XML |
| do { |
| size_t next_tgid = get_next_tgid_pclass(tgid); |
| string const tgid_str = classes.v[tgid].ptemplate.tgid; |
| |
| process_info * p = processes_root.add_process(tgid_str, tgid, next_tgid-1); |
| |
| do { |
| size_t next_tid = get_next_tid_pclass(tid); |
| |
| // build array of threads associated with this process |
| p->add_thread(classes.v[tid].ptemplate.tid, tid, next_tid-1); |
| tid = next_tid; |
| } while (tid != next_tgid); |
| tgid = next_tgid; |
| } while (tgid != nr_classes); |
| } |
| |
| void xml_utils::output_program_structure(ostream & out) |
| { |
| |
| if (cverb << vxml) |
| dump_classes(); |
| |
| if (has_separated_thread_info()) { |
| build_process_tree(); |
| processes_root.summarize_processes(extra_found_images); |
| if (cverb << vxml) |
| processes_root.dump_processes(); |
| processes_root.output_process_symbols(out); |
| } else { |
| binaries_root.summarize_binaries(extra_found_images); |
| if (cverb << vxml) |
| binaries_root.dump_binaries(); |
| binaries_root.output_binary_symbols(out); |
| } |
| } |