History

Add support for inleaving source information into disassembly view in debugger.

arr committed 9 years ago

Commit eb8b0901 parent 2b710fc4 files · +217 −59Browse files

Unified · Split

4 files
  1. src/asm/sbl_reader.cpp +28 −14
  2. src/exec/debugger.cpp +140 −31
  3. src/exec/exec.h +24 −2
  4. src/include/vm_asm.h +25 −12
src/asm/sbl_reader.cpp+28−14
@@ -598,7 +598,7 @@ namespace · 597 unmodified lines
598 return "poly<T>";598 return "poly<T>";
599 599
600 case type_class_t::array:600 case type_class_t::array:
601 return to_string(tt.resolve_table_index<type_t>(t.value_index_1), tt) + "[]";601 return "array of " + to_string(tt.resolve_table_index<type_t>(t.value_index_1), tt);
602 case type_class_t::channel:602 case type_class_t::channel:
603 return "chan of " + to_string(tt.resolve_table_index<type_t>(t.value_index_1), tt);603 return "chan of " + to_string(tt.resolve_table_index<type_t>(t.value_index_1), tt);
604 case type_class_t::list:604 case type_class_t::list:
@@ -656,13 +656,13 @@ namespace · 51 unmodified lines
656 {656 {
657 std::stringstream ss;657 std::stringstream ss;
658 658
659 if (util::has_flag(fmt, function_name_format_t::name))659 const auto inc_name = util::has_flag(fmt, function_name_format_t::name);
660 if (inc_name)
660 ss << f.name;661 ss << f.name;
661 662
662 auto has_argument = false;663 const auto inc_arguments = util::has_flag(fmt, function_name_format_t::arguments);
663 if (util::has_flag(fmt, function_name_format_t::arguments))664 if (inc_arguments)
664 {665 {
665 has_argument = true;
666 ss << '(';666 ss << '(';
667 667
668 auto first = true;668 auto first = true;
@@ -678,9 +678,11 @@ namespace · 9 unmodified lines
678 ss << ')';678 ss << ')';
679 }679 }
680 680
681 if (util::has_flag(fmt, function_name_format_t::return_type))681 // Only add the return value if it is not 'none'
682 if (util::has_flag(fmt, function_name_format_t::return_type)
683 && f.return_type.type_class != type_class_t::none)
682 {684 {
683 if (has_argument)685 if (inc_name || inc_arguments)
684 ss << ':';686 ss << ':';
685 687
686 ss << to_string(f.return_type, tt);688 ss << to_string(f.return_type, tt);
@@ -689,17 +691,17 @@ namespace · 2 unmodified lines
689 return ss.str();691 return ss.str();
690 }692 }
691 693
692 class sbl_symbol_debug final : public symbol_debug_t694 class sbl_symbol_pc_iter final : public symbol_pc_iter_t
693 {695 {
694 public:696 public:
695 sbl_symbol_debug(std::shared_ptr<sbl_file_t> f)697 sbl_symbol_pc_iter(std::shared_ptr<const sbl_file_t> f)
696 : _current_pc{ 0 }698 : _current_pc{ 0 }
697 , _file{ f }699 , _file{ f }
698 {700 {
699 assert(_file != nullptr);701 assert(_file != nullptr);
700 }702 }
701 703
702 public: // symbol_debug_t704 public: // symbol_pc_iter_t
703 void set_current_pc(disvm::runtime::vm_pc_t pc) override705 void set_current_pc(disvm::runtime::vm_pc_t pc) override
704 {706 {
705 assert(0 <= pc && pc < static_cast<int32_t>(_file->pc_table.size()));707 assert(0 <= pc && pc < static_cast<int32_t>(_file->pc_table.size()));
@@ -784,7 +786,7 @@ namespace · 78 unmodified lines
784 786
785 private:787 private:
786 runtime::vm_pc_t _current_pc;788 runtime::vm_pc_t _current_pc;
787 std::shared_ptr<sbl_file_t> _file;789 std::shared_ptr<const sbl_file_t> _file;
788 };790 };
789 791
790 class sbl_symbol_data final : public symbol_data_t792 class sbl_symbol_data final : public symbol_data_t
@@ -807,13 +809,25 @@ namespace · 16 unmodified lines
807 return _file->pc_table.size();809 return _file->pc_table.size();
808 }810 }
809 811
810 std::unique_ptr<symbol_debug_t> get_debug() override812 std::vector<function_data_t> get_functions(function_name_format_t f) const override
811 {813 {
812 return std::make_unique<sbl_symbol_debug>(_file);814 std::vector<function_data_t> funcs;
815 for (func_table_t::const_reference func : _file->func_table)
816 {
817 auto formatted_name = to_string(func, _file->type_table, f);
818 funcs.push_back({ func.entry_pc, func.limit_pc, std::move(formatted_name) });
819 }
820
821 return funcs;
822 }
823
824 std::unique_ptr<symbol_pc_iter_t> get_pc_iter() const override
825 {
826 return std::make_unique<sbl_symbol_pc_iter>(_file);
813 }827 }
814 828
815 private:829 private:
816 std::shared_ptr<sbl_file_t> _file;830 std::shared_ptr<const sbl_file_t> _file;
817 };831 };
818}832}
819 833
src/exec/debugger.cpp+140−31
@@ -123,10 +123,11 @@ namespace · 122 unmodified lines
123 auto msg = std::stringstream{};123 auto msg = std::stringstream{};
124 walk_stack(r, [&msg, &dbg](const pointer_t, const vm_pc_t pc, const vm_module_ref_t &module_ref)124 walk_stack(r, [&msg, &dbg](const pointer_t, const vm_pc_t pc, const vm_module_ref_t &module_ref)
125 {125 {
126 auto resolved_name = dbg.resolve_function_source_line(module_ref.module, pc);126 auto func_maybe = dbg.resolve_to_function_source_line(module_ref.module->vm_id, pc, pc, symbol::function_name_format_t::name);
127 if (!resolved_name.empty())127 if (!func_maybe.empty())
128 {128 {
129 msg << resolved_name;129 assert(func_maybe.size() == 1);
130 msg << module_ref.module->module_name->str() << "!" << func_maybe[0];
130 }131 }
131 else132 else
132 {133 {
@@ -336,14 +337,19 @@ namespace · 203 unmodified lines
336 void cmd_print_modules(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt)337 void cmd_print_modules(const std::vector<std::string> &, dbg_cmd_cxt_t &cxt)
337 {338 {
338 auto msg = std::stringstream{};339 auto msg = std::stringstream{};
339 cxt.vm.enum_loaded_modules([&msg](const loaded_vm_module_t &m)340 cxt.vm.enum_loaded_modules([&msg, &cxt](const loaded_vm_module_t &m)
340 {341 {
341 auto module_instance = m.module.lock();342 auto module_instance = m.module.lock();
342 if (module_instance != nullptr)343 if (module_instance != nullptr)
343 {344 {
344 msg << std::setw(3) << m.vm_id << ": "345 msg << std::setw(3) << m.vm_id << ": "
345 << std::setw(16) << module_instance->module_name->str()346 << std::setw(16) << module_instance->module_name->str()
346 << std::setw(0) << " - " << m.origin->str() << "\n";347 << std::setw(0) << " - " << m.origin->str();
348
349 if (cxt.dbg.symbols_exist(m.vm_id))
350 msg << " [symbols loaded]";
351
352 msg << "\n";
347 }353 }
348 354
349 return true;355 return true;
@@ -352,6 +358,38 @@ namespace · 2 unmodified lines
352 debug_print_info(msg.str());358 debug_print_info(msg.str());
353 }359 }
354 360
361 void cmd_print_symbols(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt)
362 {
363 if (a.size() < 2)
364 throw debug_cmd_error_t{ "Must supply a module ID" };
365
366 module_id_t vm_id;
367 try
368 {
369 vm_id = std::stoul(a[1]);
370 }
371 catch (...)
372 {
373 throw debug_cmd_error_t{ "Invalid module ID format" };
374 }
375
376 if (!cxt.dbg.symbols_exist(vm_id))
377 throw debug_cmd_error_t{ "No symbols loaded for module" };
378
379 // Interpret the last non-module ID value as the partial name to match.
380 const char *name_to_match = nullptr;
381 if (a.size() > 2)
382 name_to_match = a.back().c_str();
383
384 auto msg = std::stringstream{};
385 auto module_funcs = cxt.dbg.get_module_functions(vm_id, name_to_match);
386 msg << "Function name [PC range]\n";
387 for (auto &f : module_funcs)
388 msg << " " << f << "\n";
389
390 debug_print_info(msg.str());
391 }
392
355 void cmd_breakpoint_set(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt)393 void cmd_breakpoint_set(const std::vector<std::string> &a, dbg_cmd_cxt_t &cxt)
356 {394 {
357 if (a.size() < 2)395 if (a.size() < 2)
@@ -456,7 +494,20 @@ namespace · 98 unmodified lines
456 const auto details = cxt.dbg.controller->get_breakpoint_details(cookie);494 const auto details = cxt.dbg.controller->get_breakpoint_details(cookie);
457 msg << std::setw(3) << cookie << ": "495 msg << std::setw(3) << cookie << ": "
458 << std::setw(16) << details.module->module_name->str()496 << std::setw(16) << details.module->module_name->str()
459 << std::setw(0) << "@" << details.pc << "\n";497 << std::setw(0);
498
499 auto func_maybe = cxt.dbg.resolve_to_function_source_line(details.module->vm_id, details.pc, details.pc, symbol::function_name_format_t::name);
500 if (!func_maybe.empty())
501 {
502 assert(func_maybe.size() == 1);
503 msg << "!" << func_maybe[0];
504 }
505 else
506 {
507 msg << "@" << details.pc;
508 }
509
510 msg << "\n";
460 }511 }
461 512
462 debug_print_info(msg.str());513 debug_print_info(msg.str());
@@ -567,21 +618,24 @@ namespace · 104 unmodified lines
567 const auto &r = *cxt.current_register;618 const auto &r = *cxt.current_register;
568 const auto &code_section = r.module_ref->code_section;619 const auto &code_section = r.module_ref->code_section;
569 620
570 auto begin_index = static_cast<std::size_t>(r.pc);621 auto begin_pc = static_cast<std::size_t>(r.pc);
571 auto end_index = static_cast<std::size_t>(r.pc + 1);622 auto end_pc = static_cast<std::size_t>(r.pc);
572 if (disassemble_length < 0)623 if (disassemble_length < 0)
573 {624 {
574 const auto begin_maybe = r.pc - (-disassemble_length) + 1;625 const auto begin_maybe = r.pc - (-disassemble_length) + 1;
575 begin_index = static_cast<std::size_t>(std::max(begin_maybe, 0));626 begin_pc= static_cast<std::size_t>(std::max(begin_maybe, 0));
576 }627 }
577 else628 else
578 {629 {
579 const auto end_maybe = static_cast<std::size_t>(r.pc + disassemble_length);630 const auto end_maybe = static_cast<std::size_t>(r.pc + disassemble_length);
580 end_index = std::min(end_maybe, code_section.size());631 end_pc = std::min(end_maybe, code_section.size());
581 }632 }
582 633
634 auto resolved_pc = cxt.dbg.resolve_to_function_source_line(r.module_ref->module->vm_id, begin_pc, end_pc, symbol::function_name_format_t::name);
635 assert(resolved_pc.empty() || resolved_pc.size() == (end_pc - begin_pc + 1) && "Resolved PCs should have failed or match the PC count");
636
583 auto msg = std::stringstream{};637 auto msg = std::stringstream{};
584 for (auto i = begin_index; i < end_index; ++i)638 for (auto i = begin_pc; i < (end_pc + 1); ++i)
585 {639 {
586 auto op = code_section[i].op;640 auto op = code_section[i].op;
587 641
@@ -603,6 +657,14 @@ namespace · 15 unmodified lines
603 }657 }
604 }658 }
605 659
660 // Print source location if resolved
661 if (!resolved_pc.empty())
662 {
663 const auto & r = resolved_pc[i - begin_pc];
664 if (!r.empty())
665 msg << r << "\n";
666 }
667
606 msg << op_prefix << std::setw(5) << i << ": " << op << "\n";668 msg << op_prefix << std::setw(5) << i << ": " << op << "\n";
607 }669 }
608 670
@@ -771,6 +833,7 @@ namespace · 162 unmodified lines
771 { "r", "Print registers", nullptr, nullptr, cmd_print_registers },833 { "r", "Print registers", nullptr, nullptr, cmd_print_registers },
772 { "t", "Print all threads", nullptr, nullptr, cmd_print_threads },834 { "t", "Print all threads", nullptr, nullptr, cmd_print_threads },
773 { "m", "Print all loaded modules", nullptr, nullptr, cmd_print_modules },835 { "m", "Print all loaded modules", nullptr, nullptr, cmd_print_modules },
836 { "sym", "Print symbols for the supplied module", "sym [0-9]+ <partial function name>?", "sym 2 foo (Print symbols in module 2 containing 'foo')", cmd_print_symbols },
774 837
775 { "bs", "Set breakpoint at the specified IP in a loaded module", "bs [0-9]+ [0-9]*", "bs 3 5 (Breakpoint at IP 3 in module 5), bs 4 (Breakpoint at IP 4 in current module)", cmd_breakpoint_set },838 { "bs", "Set breakpoint at the specified IP in a loaded module", "bs [0-9]+ [0-9]*", "bs 3 5 (Breakpoint at IP 3 in module 5), bs 4 (Breakpoint at IP 4 in current module)", cmd_breakpoint_set },
776 { "bc", "Clear breakpoint(s)", "bc ([0-9]+|\\*)", "bc 3, bc *", cmd_breakpoint_clear },839 { "bc", "Clear breakpoint(s)", "bc ([0-9]+|\\*)", "bc 3, bc *", cmd_breakpoint_clear },
@@ -883,32 +946,77 @@ debugger::debugger(const debugger_options opt) · 106 unmodified lines
883 , controller{ nullptr }946 , controller{ nullptr }
884{ }947{ }
885 948
886std::string debugger::resolve_function_source_line(std::shared_ptr<const vm_module_t> m, vm_pc_t pc)949std::vector<std::string> debugger::resolve_to_function_source_line(
950 disvm::runtime::module_id_t vm_id,
951 vm_pc_t begin_pc,
952 vm_pc_t end_pc,
953 symbol::function_name_format_t fmt) const
887{954{
888 if (m == nullptr)955 auto iter = _vm_id_to_symbol_info.find(vm_id);
889 return{};956 if (iter == std::cend(_vm_id_to_symbol_info))
890
891 auto iter = _vm_id_to_symbol_debug.find(m->vm_id);
892 if (iter == std::end(_vm_id_to_symbol_debug))
893 return{};957 return{};
894 958
895 std::stringstream ss;959 auto &d = *(iter->second.iter);
896 auto &d = *(iter->second);
897 960
898 // Position the debug symbol iterator961 // Position the debug symbol iterator
899 d.set_current_pc(pc);962 d.set_current_pc(begin_pc);
963
964 std::vector<std::string> resolved_functions_by_pc(end_pc - begin_pc + 1);
965 vm_pc_t next_pc = begin_pc;
966 do
967 {
968 if (next_pc > end_pc)
969 break;
970
971 std::stringstream ss;
972 const auto function_name = d.current_function_name(fmt);
973 if (!function_name.empty())
974 ss << function_name << " at ";
975
976 const auto source_loc = d.current_source_location();
977 ss << d.get_source_by_id(source_loc.source_id)
978 << ':'
979 << source_loc.begin_line;
980
981 resolved_functions_by_pc[next_pc - begin_pc] = std::move(ss.str());
900 982
901 const auto src = d.current_source_location();983 } while (d.try_advance_pc(symbol::advance_pc_t::next_debug_statement, &next_pc));
902 984
903 ss << m->module_name->str()985 return resolved_functions_by_pc;
904 << '!'986}
905 << d.current_function_name(symbol::function_name_format_t::declaration)987
906 << " at "988std::vector<std::string> debugger::get_module_functions(disvm::runtime::module_id_t vm_id, const char *match) const
907 << d.get_source_by_id(src.source_id)989{
908 << ':'990 std::vector<std::string> funcs;
909 << src.begin_line;991 auto iter = _vm_id_to_symbol_info.find(vm_id);
992 if (iter != std::cend(_vm_id_to_symbol_info))
993 {
994 auto f_infos = iter->second.data->get_functions(symbol::function_name_format_t::declaration);
995 for (auto &fi : f_infos)
996 {
997 // Only return functions that match the supplied string
998 if (match != nullptr
999 && fi.name.find(match) == std::string::npos)
1000 continue;
1001
1002 std::stringstream ss;
1003 ss << fi.name
1004 << " ["
1005 << fi.entry_pc
1006 << ", "
1007 << fi.limit_pc
1008 << "]";
910 1009
911 return ss.str();1010 funcs.push_back(ss.str());
1011 }
1012 }
1013
1014 return funcs;
1015}
1016
1017bool debugger::symbols_exist(disvm::runtime::module_id_t vm_id) const
1018{
1019 return _vm_id_to_symbol_info.find(vm_id) != std::cend(_vm_id_to_symbol_info);
912}1020}
913 1021
914void debugger::on_load(vm_tool_controller_t &controller_, std::size_t tool_id)1022void debugger::on_load(vm_tool_controller_t &controller_, std::size_t tool_id)
@@ -1081,8 +1189,9 @@ void debugger::load_symbols(const disvm::loaded_vm_module_t &loaded_mod) · 166 unmodified lines
1081 }1189 }
1082 1190
1083 const auto vm_id = loaded_mod.vm_id;1191 const auto vm_id = loaded_mod.vm_id;
1084 assert(_vm_id_to_symbol_debug.find(vm_id) == std::end(_vm_id_to_symbol_debug) && "Module VM ID should be unique");1192 assert(_vm_id_to_symbol_info.find(vm_id) == std::end(_vm_id_to_symbol_info) && "Module VM ID should be unique");
1085 _vm_id_to_symbol_debug[vm_id] = symbol_data->get_debug();1193 auto symbol_iter = symbol_data->get_pc_iter();
1194 _vm_id_to_symbol_info[vm_id] = { std::move(symbol_data), std::move(symbol_iter) };
1086 1195
1087 std::stringstream msg;1196 std::stringstream msg;
1088 msg << "Symbol file '"1197 msg << "Symbol file '"
src/exec/exec.h+24−2
@@ -9,6 +9,9 @@ · 8 unmodified lines
9 9
10#include <cstdint>10#include <cstdint>
11#include <unordered_set>11#include <unordered_set>
12#include <vector>
13#include <string>
14#include <memory>
12#include <disvm.h>15#include <disvm.h>
13#include <vm_tools.h>16#include <vm_tools.h>
14#include <utils.h>17#include <utils.h>
@@ -31,7 +34,19 @@ class debugger final : public disvm::runtime::vm_tool_t · 16 unmodified lines
31public:34public:
32 debugger(const debugger_options);35 debugger(const debugger_options);
33 36
34 std::string resolve_function_source_line(std::shared_ptr<const disvm::runtime::vm_module_t> m, disvm::runtime::vm_pc_t pc);37 // Resolve all PCs in the range from begin and end (inclusive)
38 std::vector<std::string> resolve_to_function_source_line(
39 disvm::runtime::module_id_t vm_id,
40 disvm::runtime::vm_pc_t begin_pc,
41 disvm::runtime::vm_pc_t end_pc,
42 disvm::symbol::function_name_format_t fmt) const;
43
44 // Return all functions for the supplied module ID.
45 // Optionally match a function against the supplied string.
46 std::vector<std::string> get_module_functions(disvm::runtime::module_id_t vm_id, const char *match) const;
47
48 // Check if the supplied module ID has symbols loaded
49 bool symbols_exist(disvm::runtime::module_id_t vm_id) const;
35 50
36public: // vm_tool_t51public: // vm_tool_t
37 void on_load(disvm::runtime::vm_tool_controller_t &, std::size_t tool_id) override;52 void on_load(disvm::runtime::vm_tool_controller_t &, std::size_t tool_id) override;
@@ -48,7 +63,14 @@ private: · 10 unmodified lines
48 debugger_options _options;63 debugger_options _options;
49 std::size_t _tool_id;64 std::size_t _tool_id;
50 std::unordered_set<disvm::runtime::cookie_t> _event_cookies;65 std::unordered_set<disvm::runtime::cookie_t> _event_cookies;
51 std::unordered_map<std::size_t, std::unique_ptr<disvm::symbol::symbol_debug_t>> _vm_id_to_symbol_debug;66
67 struct symbol_info_t
68 {
69 std::unique_ptr<disvm::symbol::symbol_data_t> data;
70 std::unique_ptr<disvm::symbol::symbol_pc_iter_t> iter;
71 };
72
73 std::unordered_map<std::size_t, symbol_info_t> _vm_id_to_symbol_info;
52};74};
53 75
54#endif // _DISVM_SRC_EXEC_EXEC_H_76#endif // _DISVM_SRC_EXEC_EXEC_H_
src/include/vm_asm.h+25−12
@@ -407,6 +407,7 @@ namespace disvm · 406 unmodified lines
407 int32_t end_column;407 int32_t end_column;
408 };408 };
409 409
410 // Enumeration for formatting a function name
410 enum class function_name_format_t411 enum class function_name_format_t
411 {412 {
412 name = 1,413 name = 1,
@@ -418,20 +419,20 @@ namespace disvm · 5 unmodified lines
418 419
419 DEFINE_ENUM_FLAG_OPERATORS(function_name_format_t);420 DEFINE_ENUM_FLAG_OPERATORS(function_name_format_t);
420 421
421 class symbol_debug_t422 // Enumeration for advancing the pc
423 enum class advance_pc_t
424 {
425 current, // Return the current pc
426 next_pc,
427 next_debug_statement, // Advance to the next debug statement (i.e. source language statement)
428 };
429
430 class symbol_pc_iter_t
422 {431 {
423 public:432 public:
424 // Set the current pc433 // Set the current pc
425 virtual void set_current_pc(disvm::runtime::vm_pc_t) = 0;434 virtual void set_current_pc(disvm::runtime::vm_pc_t) = 0;
426 435
427 // Enumeration for advancing the pc
428 enum class advance_pc_t
429 {
430 current, // Return the current pc
431 next_pc,
432 next_debug_statement, // Advance to the next debug statement (i.e. source language statement)
433 };
434
435 // Advance the current pc.436 // Advance the current pc.
436 // Returns 'true' if the function was successful, otherwise 'false'.437 // Returns 'true' if the function was successful, otherwise 'false'.
437 virtual bool try_advance_pc(advance_pc_t, disvm::runtime::vm_pc_t *pc_after_advance = nullptr) = 0;438 virtual bool try_advance_pc(advance_pc_t, disvm::runtime::vm_pc_t *pc_after_advance = nullptr) = 0;
@@ -439,7 +440,7 @@ namespace disvm · 1 unmodified line
439 // Return the name of the function containing the current pc.440 // Return the name of the function containing the current pc.
440 // Optionally format the returned string.441 // Optionally format the returned string.
441 // This function will throw a 'std::runtime_error' if a function doesn't exist at the current pc.442 // This function will throw a 'std::runtime_error' if a function doesn't exist at the current pc.
442 virtual std::string current_function_name(function_name_format_t f = function_name_format_t::name) const = 0;443 virtual std::string current_function_name(function_name_format_t f) const = 0;
443 444
444 // Return the source location for the current pc445 // Return the source location for the current pc
445 virtual source_ref_t current_source_location() const = 0;446 virtual source_ref_t current_source_location() const = 0;
@@ -448,6 +449,13 @@ namespace disvm · 2 unmodified lines
448 virtual const std::string &get_source_by_id(int32_t source_id) const = 0;449 virtual const std::string &get_source_by_id(int32_t source_id) const = 0;
449 };450 };
450 451
452 struct function_data_t
453 {
454 disvm::runtime::vm_pc_t entry_pc;
455 disvm::runtime::vm_pc_t limit_pc;
456 std::string name;
457 };
458
451 class symbol_data_t459 class symbol_data_t
452 {460 {
453 public:461 public:
@@ -457,10 +465,15 @@ namespace disvm · 3 unmodified lines
457 // Get instruction count in the symbol data465 // Get instruction count in the symbol data
458 virtual size_t get_instruction_count() const = 0;466 virtual size_t get_instruction_count() const = 0;
459 467
460 // Get interface over symbol data targeted for debugging468 // Get list of functions in module.
461 virtual std::unique_ptr<symbol_debug_t> get_debug() = 0;469 // Supply a formatting enum for the function name.
470 virtual std::vector<function_data_t> get_functions(function_name_format_t f) const = 0;
471
472 // Get pc iterator interface over symbol data
473 virtual std::unique_ptr<symbol_pc_iter_t> get_pc_iter() const = 0;
462 };474 };
463 475
476 // Read in symbol data from the supplied stream
464 std::unique_ptr<symbol_data_t> read(std::istream &);477 std::unique_ptr<symbol_data_t> read(std::istream &);
465 }478 }
466 479