[Lldb-commits] [lldb] r354662 - Split up minidump register context tests
Author: labath Date: Fri Feb 22 00:51:08 2019 New Revision: 354662 URL: http://llvm.org/viewvc/llvm-project?rev=354662&view=rev Log: Split up minidump register context tests The tests were doing two somewhat independent things: - checking that the registers can be retrieved from the minidump file - checking that they can be converted into a form suitable for consumption by lldb The first thing requires a minidump file (but it's independent of other lldb structures), while the second one does not require a minidump file (but it needs lldb register info structures). Splitting this into two tests gives an opportunity to write more detailed tests, and allows the two pieces of functionality to be moved into different packages, if that proves to be necessary. Added: lldb/trunk/unittests/Process/minidump/RegisterContextMinidumpTest.cpp Modified: lldb/trunk/unittests/Process/minidump/CMakeLists.txt lldb/trunk/unittests/Process/minidump/MinidumpParserTest.cpp Modified: lldb/trunk/unittests/Process/minidump/CMakeLists.txt URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/Process/minidump/CMakeLists.txt?rev=354662&r1=354661&r2=354662&view=diff == --- lldb/trunk/unittests/Process/minidump/CMakeLists.txt (original) +++ lldb/trunk/unittests/Process/minidump/CMakeLists.txt Fri Feb 22 00:51:08 2019 @@ -1,5 +1,6 @@ add_lldb_unittest(LLDBMinidumpTests MinidumpParserTest.cpp + RegisterContextMinidumpTest.cpp LINK_LIBS lldbCore Modified: lldb/trunk/unittests/Process/minidump/MinidumpParserTest.cpp URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/Process/minidump/MinidumpParserTest.cpp?rev=354662&r1=354661&r2=354662&view=diff == --- lldb/trunk/unittests/Process/minidump/MinidumpParserTest.cpp (original) +++ lldb/trunk/unittests/Process/minidump/MinidumpParserTest.cpp Fri Feb 22 00:51:08 2019 @@ -6,13 +6,10 @@ // //===--===// -#include "Plugins/Process/Utility/RegisterContextLinux_i386.h" -#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h" #include "Plugins/Process/minidump/MinidumpParser.h" #include "Plugins/Process/minidump/MinidumpTypes.h" #include "Plugins/Process/minidump/RegisterContextMinidump_x86_32.h" #include "Plugins/Process/minidump/RegisterContextMinidump_x86_64.h" - #include "TestingSupport/TestUtilities.h" #include "lldb/Host/FileSystem.h" #include "lldb/Target/MemoryRegionInfo.h" @@ -490,138 +487,102 @@ TEST_F(MinidumpParserTest, GetModuleList #define REG_VAL32(x) *(reinterpret_cast(x)) #define REG_VAL64(x) *(reinterpret_cast(x)) -TEST_F(MinidumpParserTest, ConvertMinidumpContext_x86_32) { +TEST_F(MinidumpParserTest, GetThreadContext_x86_32) { SetUpData("linux-i386.dmp"); llvm::ArrayRef thread_list = parser->GetThreads(); const MinidumpThread thread = thread_list[0]; llvm::ArrayRef registers(parser->GetThreadContext(thread)); + const MinidumpContext_x86_32 *context; + EXPECT_TRUE(consumeObject(registers, context).Success()); - ArchSpec arch = parser->GetArchitecture(); - auto reg_interface = llvm::make_unique(arch); - lldb::DataBufferSP buf = - ConvertMinidumpContext_x86_32(registers, reg_interface.get()); - ASSERT_EQ(reg_interface->GetGPRSize(), buf->GetByteSize()); - - const RegisterInfo *reg_info = reg_interface->GetRegisterInfo(); - - std::map reg_values; - - reg_values[lldb_eax_i386] = 0x; - reg_values[lldb_ebx_i386] = 0xf7778000; - reg_values[lldb_ecx_i386] = 0x0001; - reg_values[lldb_edx_i386] = 0xff9dd4a3; - reg_values[lldb_edi_i386] = 0x080482a8; - reg_values[lldb_esi_i386] = 0xff9dd55c; - reg_values[lldb_ebp_i386] = 0xff9dd53c; - reg_values[lldb_esp_i386] = 0xff9dd52c; - reg_values[lldb_eip_i386] = 0x080482a0; - reg_values[lldb_eflags_i386] = 0x00010282; - reg_values[lldb_cs_i386] = 0x0023; - reg_values[lldb_fs_i386] = 0x; - reg_values[lldb_gs_i386] = 0x0063; - reg_values[lldb_ss_i386] = 0x002b; - reg_values[lldb_ds_i386] = 0x002b; - reg_values[lldb_es_i386] = 0x002b; - - for (uint32_t reg_index = 0; reg_index < reg_interface->GetRegisterCount(); - ++reg_index) { -if (reg_values.find(reg_index) != reg_values.end()) { - EXPECT_EQ(reg_values[reg_index], -REG_VAL32(buf->GetBytes() + reg_info[reg_index].byte_offset)); -} - } + EXPECT_EQ(MinidumpContext_x86_32_Flags(uint32_t(context->context_flags)), +MinidumpContext_x86_32_Flags::x86_32_Flag | +MinidumpContext_x86_32_Flags::Full | +MinidumpContext_x86_32_Flags::FloatingPoint); + + EXPECT_EQ(0xu, context->eax); + EXPECT_EQ(0xf7778000u, context->ebx); + EXPECT_EQ(0x0001u, context->ecx); + EXPECT_EQ(0xff9dd4a3u, context->edx); + EXPECT_EQ(0x080482a8u, context->edi); + EX
[Lldb-commits] [lldb] r354668 - Avoid two-stage initialization of MinidumpParser
Author: labath Date: Fri Feb 22 05:36:01 2019 New Revision: 354668 URL: http://llvm.org/viewvc/llvm-project?rev=354668&view=rev Log: Avoid two-stage initialization of MinidumpParser remove the Initialize function, move the things that can fail into the static factory function. The factory function now returns Expected instead of Optional so that it can give a reason why creation failed. Modified: lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.cpp lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.h lldb/trunk/source/Plugins/Process/minidump/ProcessMinidump.cpp lldb/trunk/source/Plugins/Process/minidump/ProcessMinidump.h lldb/trunk/unittests/Process/minidump/CMakeLists.txt lldb/trunk/unittests/Process/minidump/MinidumpParserTest.cpp Modified: lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.cpp URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.cpp?rev=354668&r1=354667&r2=354668&view=diff == --- lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.cpp (original) +++ lldb/trunk/source/Plugins/Process/minidump/MinidumpParser.cpp Fri Feb 22 05:36:01 2019 @@ -23,16 +23,106 @@ using namespace lldb_private; using namespace minidump; -llvm::Optional -MinidumpParser::Create(const lldb::DataBufferSP &data_buf_sp) { - if (data_buf_sp->GetByteSize() < sizeof(MinidumpHeader)) { -return llvm::None; +static llvm::Error stringError(llvm::StringRef Err) { + return llvm::make_error(Err, + llvm::inconvertibleErrorCode()); +} + +llvm::Expected +MinidumpParser::Create(const lldb::DataBufferSP &data_sp) { + if (data_sp->GetByteSize() < sizeof(MinidumpHeader)) +return stringError("Buffer too small."); + + llvm::ArrayRef header_data(data_sp->GetBytes(), + sizeof(MinidumpHeader)); + const MinidumpHeader *header = MinidumpHeader::Parse(header_data); + if (!header) +return stringError("invalid minidump: can't parse the header"); + + // A minidump without at least one stream is clearly ill-formed + if (header->streams_count == 0) +return stringError("invalid minidump: no streams present"); + + struct FileRange { +uint32_t offset = 0; +uint32_t size = 0; + +FileRange(uint32_t offset, uint32_t size) : offset(offset), size(size) {} +uint32_t end() const { return offset + size; } + }; + + const uint32_t file_size = data_sp->GetByteSize(); + + // Build a global minidump file map, checking for: + // - overlapping streams/data structures + // - truncation (streams pointing past the end of file) + std::vector minidump_map; + + minidump_map.emplace_back(0, sizeof(MinidumpHeader)); + + // Add the directory entries to the file map + FileRange directory_range(header->stream_directory_rva, +header->streams_count * sizeof(MinidumpDirectory)); + if (directory_range.end() > file_size) +return stringError("invalid minidump: truncated streams directory"); + minidump_map.push_back(directory_range); + + llvm::DenseMap directory_map; + + // Parse stream directory entries + llvm::ArrayRef directory_data( + data_sp->GetBytes() + directory_range.offset, directory_range.size); + for (uint32_t i = 0; i < header->streams_count; ++i) { +const MinidumpDirectory *directory_entry = nullptr; +Status error = consumeObject(directory_data, directory_entry); +if (error.Fail()) + return error.ToError(); +if (directory_entry->stream_type == 0) { + // Ignore dummy streams (technically ill-formed, but a number of + // existing minidumps seem to contain such streams) + if (directory_entry->location.data_size == 0) +continue; + return stringError("invalid minidump: bad stream type"); +} +// Update the streams map, checking for duplicate stream types +if (!directory_map + .insert({directory_entry->stream_type, directory_entry->location}) + .second) + return stringError("invalid minidump: duplicate stream type"); + +// Ignore the zero-length streams for layout checks +if (directory_entry->location.data_size != 0) { + minidump_map.emplace_back(directory_entry->location.rva, +directory_entry->location.data_size); +} } - return MinidumpParser(data_buf_sp); + + // Sort the file map ranges by start offset + llvm::sort(minidump_map.begin(), minidump_map.end(), + [](const FileRange &a, const FileRange &b) { + return a.offset < b.offset; + }); + + // Check for overlapping streams/data structures + for (size_t i = 1; i < minidump_map.size(); ++i) { +const auto &prev_range = minidump_map[i - 1]; +if (prev_range.end() > minidump_map[i].offset) + return stringError("invalid minidump: overlapping streams"); + } + + // Check for streams
[Lldb-commits] [lldb] r354702 - When deserializing breakpoints some options may not be present.
Author: jingham Date: Fri Feb 22 15:54:11 2019 New Revision: 354702 URL: http://llvm.org/viewvc/llvm-project?rev=354702&view=rev Log: When deserializing breakpoints some options may not be present. The deserializer was not handling this case. For now we just accept the absent option, and set it to the breakpoint default. This will be more important if/when I figure out how to serialize the options set on breakpont locations. Modified: lldb/trunk/packages/Python/lldbsuite/test/functionalities/breakpoint/serialize/TestBreakpointSerialization.py lldb/trunk/source/Breakpoint/BreakpointOptions.cpp Modified: lldb/trunk/packages/Python/lldbsuite/test/functionalities/breakpoint/serialize/TestBreakpointSerialization.py URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/breakpoint/serialize/TestBreakpointSerialization.py?rev=354702&r1=354701&r2=354702&view=diff == --- lldb/trunk/packages/Python/lldbsuite/test/functionalities/breakpoint/serialize/TestBreakpointSerialization.py (original) +++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/breakpoint/serialize/TestBreakpointSerialization.py Fri Feb 22 15:54:11 2019 @@ -17,6 +17,7 @@ from lldbsuite.test import lldbutil class BreakpointSerialization(TestBase): mydir = TestBase.compute_mydir(__file__) +NO_DEBUG_INFO_TESTCASE = True @add_test_categories(['pyapi']) def test_resolvers(self): @@ -196,6 +197,11 @@ class BreakpointSerialization(TestBase): bkpt.SetThreadName("grubby") source_bps.Append(bkpt) +bkpt = self.orig_target.BreakpointCreateByName("blubby", lldb.eFunctionNameTypeAuto, empty_module_list, empty_cu_list) +bkpt.SetCondition("gonna remove this") +bkpt.SetCondition("") +source_bps.Append(bkpt) + bkpt = self.orig_target.BreakpointCreateByName("blubby", lldb.eFunctionNameTypeFull, empty_module_list,empty_cu_list) bkpt.SetCondition("something != something_else") bkpt.SetQueueName("grubby") Modified: lldb/trunk/source/Breakpoint/BreakpointOptions.cpp URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Breakpoint/BreakpointOptions.cpp?rev=354702&r1=354701&r2=354702&view=diff == --- lldb/trunk/source/Breakpoint/BreakpointOptions.cpp (original) +++ lldb/trunk/source/Breakpoint/BreakpointOptions.cpp Fri Feb 22 15:54:11 2019 @@ -253,55 +253,50 @@ std::unique_ptr Break const char *key = GetKey(OptionNames::EnabledState); bool success; - if (key) { + if (key && options_dict.HasKey(key)) { success = options_dict.GetValueForKeyAsBoolean(key, enabled); if (!success) { - error.SetErrorStringWithFormat("%s key is not a boolean.", - GetKey(OptionNames::EnabledState)); + error.SetErrorStringWithFormat("%s key is not a boolean.", key); return nullptr; } set_options.Set(eEnabled); } key = GetKey(OptionNames::OneShotState); - if (key) { + if (key && options_dict.HasKey(key)) { success = options_dict.GetValueForKeyAsBoolean(key, one_shot); if (!success) { - error.SetErrorStringWithFormat("%s key is not a boolean.", - GetKey(OptionNames::OneShotState)); + error.SetErrorStringWithFormat("%s key is not a boolean.", key); return nullptr; } set_options.Set(eOneShot); } key = GetKey(OptionNames::AutoContinue); - if (key) { + if (key && options_dict.HasKey(key)) { success = options_dict.GetValueForKeyAsBoolean(key, auto_continue); if (!success) { - error.SetErrorStringWithFormat("%s key is not a boolean.", - GetKey(OptionNames::AutoContinue)); + error.SetErrorStringWithFormat("%s key is not a boolean.", key); return nullptr; } set_options.Set(eAutoContinue); } key = GetKey(OptionNames::IgnoreCount); - if (key) { + if (key && options_dict.HasKey(key)) { success = options_dict.GetValueForKeyAsInteger(key, ignore_count); if (!success) { - error.SetErrorStringWithFormat("%s key is not an integer.", - GetKey(OptionNames::IgnoreCount)); + error.SetErrorStringWithFormat("%s key is not an integer.", key); return nullptr; } set_options.Set(eIgnoreCount); } key = GetKey(OptionNames::ConditionText); - if (key) { + if (key && options_dict.HasKey(key)) { success = options_dict.GetValueForKeyAsString(key, condition_ref); if (!success) { - error.SetErrorStringWithFormat("%s key is not an string.", - GetKey(OptionNames::ConditionText)); + error.SetErrorStringWithFormat("%s key is not an string.", key); return nullptr; } set_options.Set(eCondition);
[Lldb-commits] [lldb] r354706 - Make sure that stop-hooks run asynchronously.
Author: jingham Date: Fri Feb 22 16:13:25 2019 New Revision: 354706 URL: http://llvm.org/viewvc/llvm-project?rev=354706&view=rev Log: Make sure that stop-hooks run asynchronously. They aren't designed to nest recursively, so this will prevent that. Also add a --auto-continue flag, putting "continue" in the stop hook makes the stop hooks fight one another in multi-threaded programs. Also allow more than one -o options so you can make more complex stop hooks w/o having to go into the editor. Differential Revision: https://reviews.llvm.org/D58394 Modified: lldb/trunk/include/lldb/Target/Process.h lldb/trunk/include/lldb/Target/Target.h lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-2.lldbinit lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-3.lldbinit lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-threads-1.lldbinit lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-threads-2.lldbinit lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-threads.cpp lldb/trunk/lit/ExecControl/StopHook/stop-hook-threads.test lldb/trunk/lit/ExecControl/StopHook/stop-hook.test lldb/trunk/source/Commands/CommandObjectTarget.cpp lldb/trunk/source/Target/Process.cpp lldb/trunk/source/Target/Target.cpp Modified: lldb/trunk/include/lldb/Target/Process.h URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Target/Process.h?rev=354706&r1=354705&r2=354706&view=diff == --- lldb/trunk/include/lldb/Target/Process.h (original) +++ lldb/trunk/include/lldb/Target/Process.h Fri Feb 22 16:13:25 2019 @@ -2519,6 +2519,8 @@ public: /// //-- void RestoreProcessEvents(); + + bool IsHijackedForSynchronousResume(); const lldb::ABISP &GetABI(); Modified: lldb/trunk/include/lldb/Target/Target.h URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Target/Target.h?rev=354706&r1=354705&r2=354706&view=diff == --- lldb/trunk/include/lldb/Target/Target.h (original) +++ lldb/trunk/include/lldb/Target/Target.h Fri Feb 22 16:13:25 2019 @@ -1153,6 +1153,10 @@ public: void SetIsActive(bool is_active) { m_active = is_active; } +void SetAutoContinue(bool auto_continue) {m_auto_continue = auto_continue;} + +bool GetAutoContinue() const { return m_auto_continue; } + void GetDescription(Stream *s, lldb::DescriptionLevel level) const; private: @@ -1160,7 +1164,8 @@ public: StringList m_commands; lldb::SymbolContextSpecifierSP m_specifier_sp; std::unique_ptr m_thread_spec_up; -bool m_active; +bool m_active = true; +bool m_auto_continue = false; // Use CreateStopHook to make a new empty stop hook. The GetCommandPointer // and fill it with commands, and SetSpecifier to set the specifier shared Modified: lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-2.lldbinit URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-2.lldbinit?rev=354706&r1=354705&r2=354706&view=diff == --- lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-2.lldbinit (original) +++ lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-2.lldbinit Fri Feb 22 16:13:25 2019 @@ -1 +1 @@ -target stop-hook add -f stop-hook.c -l 29 -e 34 -o "expr ptr" \ No newline at end of file +target stop-hook add -f stop-hook.c -l 29 -e 34 -o "expr ptr" Modified: lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-3.lldbinit URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-3.lldbinit?rev=354706&r1=354705&r2=354706&view=diff == --- lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-3.lldbinit (original) +++ lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-3.lldbinit Fri Feb 22 16:13:25 2019 @@ -1,3 +1,3 @@ target stop-hook add -f stop-hook.c -l 29 -e 34 expr ptr -DONE \ No newline at end of file +DONE Modified: lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-threads-1.lldbinit URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-threads-1.lldbinit?rev=354706&r1=354705&r2=354706&view=diff == --- lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-threads-1.lldbinit (original) +++ lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-threads-1.lldbinit Fri Feb 22 16:13:25 2019 @@ -1,7 +1,7 @@ +break set -f stop-hook-threads.cpp -p "Break here to set up the stop hook" break set -f stop-hook-threads.cpp -p "Break here to test that the stop-hook" run -target stop-hook add -frame variable --show-globals g_val +target stop-hook add -G true +expr lldb_val += 1 thr
[Lldb-commits] [PATCH] D58394: Add --auto-continue to stop-hooks, fix up a few tests
This revision was automatically updated to reflect the committed changes. Closed by commit rL354706: Make sure that stop-hooks run asynchronously. (authored by jingham, committed by ). Herald added a project: LLVM. Herald added a subscriber: llvm-commits. Changed prior to commit: https://reviews.llvm.org/D58394?vs=187893&id=188006#toc Repository: rL LLVM CHANGES SINCE LAST ACTION https://reviews.llvm.org/D58394/new/ https://reviews.llvm.org/D58394 Files: lldb/trunk/include/lldb/Target/Process.h lldb/trunk/include/lldb/Target/Target.h lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-2.lldbinit lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-3.lldbinit lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-threads-1.lldbinit lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-threads-2.lldbinit lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-threads.cpp lldb/trunk/lit/ExecControl/StopHook/stop-hook-threads.test lldb/trunk/lit/ExecControl/StopHook/stop-hook.test lldb/trunk/source/Commands/CommandObjectTarget.cpp lldb/trunk/source/Target/Process.cpp lldb/trunk/source/Target/Target.cpp Index: lldb/trunk/source/Commands/CommandObjectTarget.cpp === --- lldb/trunk/source/Commands/CommandObjectTarget.cpp +++ lldb/trunk/source/Commands/CommandObjectTarget.cpp @@ -4555,7 +4555,7 @@ static constexpr OptionDefinition g_target_stop_hook_add_options[] = { // clang-format off - { LLDB_OPT_SET_ALL, false, "one-liner",'o', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeOneLiner, "Specify a one-line breakpoint command inline. Be sure to surround it with quotes." }, + { LLDB_OPT_SET_ALL, false, "one-liner",'o', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeOneLiner, "Add a command for the stop hook. Can be specified more than once, and commands will be run in the order they appear." }, { LLDB_OPT_SET_ALL, false, "shlib",'s', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eModuleCompletion, eArgTypeShlibName,"Set the module within which the stop-hook is to be run." }, { LLDB_OPT_SET_ALL, false, "thread-index", 'x', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeThreadIndex, "The stop hook is run only for the thread whose index matches this argument." }, { LLDB_OPT_SET_ALL, false, "thread-id",'t', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeThreadID, "The stop hook is run only for the thread whose TID matches this argument." }, @@ -4566,6 +4566,7 @@ { LLDB_OPT_SET_1, false, "end-line", 'e', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeLineNum, "Set the end of the line range for which the stop-hook is to be run." }, { LLDB_OPT_SET_2, false, "classname",'c', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeClassName,"Specify the class within which the stop-hook is to be run." }, { LLDB_OPT_SET_3, false, "name", 'n', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eSymbolCompletion, eArgTypeFunctionName, "Set the function name within which the stop hook will be run." }, + { LLDB_OPT_SET_ALL, false, "auto-continue",'G', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "The breakpoint will auto-continue after running its commands." }, // clang-format on }; @@ -4606,6 +4607,17 @@ m_sym_ctx_specified = true; break; + case 'G': { +bool value, success; +value = OptionArgParser::ToBoolean(option_arg, false, &success); +if (success) { + m_auto_continue = value; +} else + error.SetErrorStringWithFormat( + "invalid boolean value '%s' passed for -G option", + option_arg.str().c_str()); + } + break; case 'l': if (option_arg.getAsInteger(0, m_line_start)) { error.SetErrorStringWithFormat("invalid start line number: \"%s\"", @@ -4661,7 +4673,7 @@ case 'o': m_use_one_liner = true; -m_one_liner = option_arg; +m_one_liner.push_back(option_arg); break; default: @@ -4690,6 +4702,7 @@ m_use_one_liner = false; m_one_liner.clear(); + m_auto_continue = false; } std::string m_class_name; @@ -4708,7 +4721,8 @@ bool m_thread_specified; // Instance variables to hold the values for one_liner options. bool m_use_one_liner; -std::string m_one_liner; +std::vector m_one_liner; +bool m_auto_continue; }; CommandObjectTargetStopHookAdd(CommandInterpreter &interpreter) @@ -4833,10 +4847,13 @@ new_hook_sp->SetThreadSpecifier(thread_spec); } +
[Lldb-commits] [lldb] r354711 - Revert r354706 - lit touched my thigh
Author: jingham Date: Fri Feb 22 17:08:17 2019 New Revision: 354711 URL: http://llvm.org/viewvc/llvm-project?rev=354711&view=rev Log: Revert r354706 - lit touched my thigh Modified: lldb/trunk/include/lldb/Target/Process.h lldb/trunk/include/lldb/Target/Target.h lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-2.lldbinit lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-3.lldbinit lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-threads-1.lldbinit lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-threads-2.lldbinit lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-threads.cpp lldb/trunk/lit/ExecControl/StopHook/stop-hook-threads.test lldb/trunk/lit/ExecControl/StopHook/stop-hook.test lldb/trunk/source/Commands/CommandObjectTarget.cpp lldb/trunk/source/Target/Process.cpp lldb/trunk/source/Target/Target.cpp Modified: lldb/trunk/include/lldb/Target/Process.h URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Target/Process.h?rev=354711&r1=354710&r2=354711&view=diff == --- lldb/trunk/include/lldb/Target/Process.h (original) +++ lldb/trunk/include/lldb/Target/Process.h Fri Feb 22 17:08:17 2019 @@ -2519,8 +2519,6 @@ public: /// //-- void RestoreProcessEvents(); - - bool IsHijackedForSynchronousResume(); const lldb::ABISP &GetABI(); Modified: lldb/trunk/include/lldb/Target/Target.h URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Target/Target.h?rev=354711&r1=354710&r2=354711&view=diff == --- lldb/trunk/include/lldb/Target/Target.h (original) +++ lldb/trunk/include/lldb/Target/Target.h Fri Feb 22 17:08:17 2019 @@ -1153,10 +1153,6 @@ public: void SetIsActive(bool is_active) { m_active = is_active; } -void SetAutoContinue(bool auto_continue) {m_auto_continue = auto_continue;} - -bool GetAutoContinue() const { return m_auto_continue; } - void GetDescription(Stream *s, lldb::DescriptionLevel level) const; private: @@ -1164,8 +1160,7 @@ public: StringList m_commands; lldb::SymbolContextSpecifierSP m_specifier_sp; std::unique_ptr m_thread_spec_up; -bool m_active = true; -bool m_auto_continue = false; +bool m_active; // Use CreateStopHook to make a new empty stop hook. The GetCommandPointer // and fill it with commands, and SetSpecifier to set the specifier shared Modified: lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-2.lldbinit URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-2.lldbinit?rev=354711&r1=354710&r2=354711&view=diff == --- lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-2.lldbinit (original) +++ lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-2.lldbinit Fri Feb 22 17:08:17 2019 @@ -1 +1 @@ -target stop-hook add -f stop-hook.c -l 29 -e 34 -o "expr ptr" +target stop-hook add -f stop-hook.c -l 29 -e 34 -o "expr ptr" \ No newline at end of file Modified: lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-3.lldbinit URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-3.lldbinit?rev=354711&r1=354710&r2=354711&view=diff == --- lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-3.lldbinit (original) +++ lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-3.lldbinit Fri Feb 22 17:08:17 2019 @@ -1,3 +1,3 @@ target stop-hook add -f stop-hook.c -l 29 -e 34 expr ptr -DONE +DONE \ No newline at end of file Modified: lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-threads-1.lldbinit URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-threads-1.lldbinit?rev=354711&r1=354710&r2=354711&view=diff == --- lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-threads-1.lldbinit (original) +++ lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-threads-1.lldbinit Fri Feb 22 17:08:17 2019 @@ -1,7 +1,7 @@ -break set -f stop-hook-threads.cpp -p "Break here to set up the stop hook" break set -f stop-hook-threads.cpp -p "Break here to test that the stop-hook" run -target stop-hook add -G true -expr lldb_val += 1 +target stop-hook add +frame variable --show-globals g_val thread list +continue DONE Modified: lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-threads-2.lldbinit URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/ExecControl/StopHook/Inputs/stop-hook-threads-2.lldbinit?rev=354711&r1=354710&r2=354711&view=diff == --- lldb/trunk/lit/ExecControl/StopHook
[Lldb-commits] [PATCH] D58564: [Reproducers] Add command provider
JDevlieghere created this revision. JDevlieghere added reviewers: labath, davide, aprantl. JDevlieghere added a project: LLDB. Herald added a subscriber: jdoerfert. Add a provider to the command interpreter. Essentially this writes all the commands to a file which is used during replay as input to the command interpreter. Repository: rLLDB LLDB https://reviews.llvm.org/D58564 Files: lldb/include/lldb/Interpreter/CommandInterpreter.h lldb/source/Commands/CommandObjectReproducer.cpp lldb/source/Interpreter/CommandInterpreter.cpp Index: lldb/source/Interpreter/CommandInterpreter.cpp === --- lldb/source/Interpreter/CommandInterpreter.cpp +++ lldb/source/Interpreter/CommandInterpreter.cpp @@ -45,6 +45,7 @@ #include "lldb/Core/PluginManager.h" #include "lldb/Core/StreamFile.h" #include "lldb/Utility/Log.h" +#include "lldb/Utility/Reproducer.h" #include "lldb/Utility/State.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/Timer.h" @@ -74,6 +75,7 @@ using namespace lldb; using namespace lldb_private; +using namespace llvm; static const char *k_white_space = " \t\v"; @@ -116,6 +118,43 @@ eEchoCommentCommands = 5 }; +class lldb_private::CommandProvider +: public repro::Provider { +public: + typedef CommandProviderInfo info; + + CommandProvider(const FileSpec &directory) : Provider(directory) {} + + void CaptureCommand(std::string command) { +m_commands.push_back(std::move(command)); + } + + void Keep() override { +FileSpec file = +GetRoot().CopyByAppendingPathComponent("command-interpreter.txt"); + +std::error_code ec; +llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::F_Text); + +if (ec) + return; + +for (auto &command : m_commands) + os << command << '\n'; + } + + void Discard() override {} + + static char ID; + +private: + std::vector m_commands; +}; + +char CommandProvider::ID = 0; +const char *CommandProviderInfo::name = "command-interpreter"; +const char *CommandProviderInfo::file = "command-interpreter.txt"; + ConstString &CommandInterpreter::GetStaticBroadcasterClass() { static ConstString class_name("lldb.commandInterpreter"); return class_name; @@ -141,6 +180,9 @@ SetEventName(eBroadcastBitQuitCommandReceived, "quit"); CheckInWithManager(); m_collection_sp->Initialize(g_properties); + + if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) +m_provider = &g->GetOrCreate(); } bool CommandInterpreter::GetExpandRegexAliases() const { @@ -1694,6 +1736,9 @@ Status error(PreprocessCommand(command_string)); + if (m_provider) +m_provider->CaptureCommand(original_command_string); + if (error.Fail()) { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); Index: lldb/source/Commands/CommandObjectReproducer.cpp === --- lldb/source/Commands/CommandObjectReproducer.cpp +++ lldb/source/Commands/CommandObjectReproducer.cpp @@ -37,8 +37,13 @@ auto &r = repro::Reproducer::Instance(); if (auto generator = r.GetGenerator()) { generator->Keep(); +} else if (r.GetLoader()) { + // Make this operation a NOP in replay mode. + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); } else { result.AppendErrorWithFormat("Unable to get the reproducer generator"); + result.SetStatus(eReturnStatusFailed); return false; } Index: lldb/include/lldb/Interpreter/CommandInterpreter.h === --- lldb/include/lldb/Interpreter/CommandInterpreter.h +++ lldb/include/lldb/Interpreter/CommandInterpreter.h @@ -27,6 +27,12 @@ namespace lldb_private { +class CommandProvider; +struct CommandProviderInfo { + static const char *name; + static const char *file; +}; + class CommandInterpreterRunOptions { public: //-- @@ -606,6 +612,9 @@ bool m_quit_requested; bool m_stopped_for_crash; + // Reproducer provider. + CommandProvider *m_provider = nullptr; + // The exit code the user has requested when calling the 'quit' command. // No value means the user hasn't set a custom exit code so far. llvm::Optional m_quit_exit_code; ___ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
[Lldb-commits] [PATCH] D57475: [Reproducers] Add SBReproducer macros
JDevlieghere updated this revision to Diff 188015. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D57475/new/ https://reviews.llvm.org/D57475 Files: lldb/source/API/SBAddress.cpp lldb/source/API/SBAttachInfo.cpp lldb/source/API/SBBlock.cpp lldb/source/API/SBBreakpoint.cpp lldb/source/API/SBBreakpointLocation.cpp lldb/source/API/SBBreakpointName.cpp lldb/source/API/SBBroadcaster.cpp lldb/source/API/SBCommandInterpreter.cpp lldb/source/API/SBCommandReturnObject.cpp lldb/source/API/SBCommunication.cpp lldb/source/API/SBCompileUnit.cpp lldb/source/API/SBData.cpp lldb/source/API/SBDebugger.cpp lldb/source/API/SBDeclaration.cpp lldb/source/API/SBError.cpp lldb/source/API/SBEvent.cpp lldb/source/API/SBExecutionContext.cpp lldb/source/API/SBExpressionOptions.cpp lldb/source/API/SBFileSpec.cpp lldb/source/API/SBFileSpecList.cpp lldb/source/API/SBFrame.cpp lldb/source/API/SBFunction.cpp lldb/source/API/SBHostOS.cpp lldb/source/API/SBInstruction.cpp lldb/source/API/SBInstructionList.cpp lldb/source/API/SBLanguageRuntime.cpp lldb/source/API/SBLaunchInfo.cpp lldb/source/API/SBLineEntry.cpp lldb/source/API/SBListener.cpp lldb/source/API/SBMemoryRegionInfo.cpp lldb/source/API/SBMemoryRegionInfoList.cpp lldb/source/API/SBModule.cpp lldb/source/API/SBModuleSpec.cpp lldb/source/API/SBPlatform.cpp lldb/source/API/SBProcess.cpp lldb/source/API/SBProcessInfo.cpp lldb/source/API/SBQueue.cpp lldb/source/API/SBQueueItem.cpp lldb/source/API/SBReproducer.cpp lldb/source/API/SBSection.cpp lldb/source/API/SBSourceManager.cpp lldb/source/API/SBStream.cpp lldb/source/API/SBStringList.cpp lldb/source/API/SBStructuredData.cpp lldb/source/API/SBSymbol.cpp lldb/source/API/SBSymbolContext.cpp lldb/source/API/SBSymbolContextList.cpp lldb/source/API/SBTarget.cpp lldb/source/API/SBThread.cpp lldb/source/API/SBThreadCollection.cpp lldb/source/API/SBThreadPlan.cpp lldb/source/API/SBTrace.cpp lldb/source/API/SBTraceOptions.cpp lldb/source/API/SBType.cpp lldb/source/API/SBTypeCategory.cpp lldb/source/API/SBTypeEnumMember.cpp lldb/source/API/SBTypeFilter.cpp lldb/source/API/SBTypeFormat.cpp lldb/source/API/SBTypeNameSpecifier.cpp lldb/source/API/SBTypeSummary.cpp lldb/source/API/SBTypeSynthetic.cpp lldb/source/API/SBUnixSignals.cpp lldb/source/API/SBValue.cpp lldb/source/API/SBValueList.cpp lldb/source/API/SBVariablesOptions.cpp lldb/source/API/SBWatchpoint.cpp ___ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
[Lldb-commits] [PATCH] D58565: Enable replay from SBRepro
JDevlieghere created this revision. JDevlieghere added a reviewer: davide. JDevlieghere added a project: LLDB. JDevlieghere added a parent revision: D57475: [Reproducers] Add SBReproducer macros. Repository: rLLDB LLDB https://reviews.llvm.org/D58565 Files: lldb/lit/Reproducer/Inputs/FileReplay.in lldb/lit/Reproducer/Inputs/GDBRemoteReplay.in lldb/lit/Reproducer/TestFileRepro.test lldb/lit/Reproducer/TestGDBRemoteRepro.test lldb/source/API/SBReproducer.cpp lldb/tools/driver/Driver.cpp Index: lldb/tools/driver/Driver.cpp === --- lldb/tools/driver/Driver.cpp +++ lldb/tools/driver/Driver.cpp @@ -905,7 +905,7 @@ WithColor::error() << "reproducer replay failed: " << error << '\n'; return 1; } -// FIXME: Return once SBReproducer::Replay actually performs the replay. +return 0; } SBError error = SBDebugger::InitializeWithErrorHandling(); Index: lldb/source/API/SBReproducer.cpp === --- lldb/source/API/SBReproducer.cpp +++ lldb/source/API/SBReproducer.cpp @@ -2918,8 +2918,6 @@ return error.c_str(); } - // FIXME: Enable the following code once the SB reproducer has landed. -#if 0 FileSpec file = loader->GetFile(); if (!file) { error = "unable to get replay data from reproducer."; @@ -2928,7 +2926,6 @@ SBRegistry registry; registry.Replay(file); -#endif return nullptr; } Index: lldb/lit/Reproducer/TestGDBRemoteRepro.test === --- lldb/lit/Reproducer/TestGDBRemoteRepro.test +++ lldb/lit/Reproducer/TestGDBRemoteRepro.test @@ -7,8 +7,8 @@ # that the string "testing" is not printed. # RUN: %clang %S/Inputs/simple.c -g -o %t.out -# RUN: %lldb -x -b -s %S/Inputs/GDBRemoteCapture.in --capture %t.repro -- %t.out | FileCheck %s --check-prefix CHECK --check-prefix CAPTURE -# RUN: %lldb -x -b -s %S/Inputs/GDBRemoteReplay.in --replay %t.repro -- %t.out | FileCheck %s --check-prefix CHECK --check-prefix REPLAY +# RUN: %lldb -x -b -s %S/Inputs/GDBRemoteCapture.in --capture %t.repro %t.out | FileCheck %s --check-prefix CHECK --check-prefix CAPTURE +# RUN: %lldb --replay %t.repro | FileCheck %s --check-prefix CHECK --check-prefix REPLAY # CHECK: Breakpoint 1 # CHECK: Process {{.*}} stopped Index: lldb/lit/Reproducer/TestFileRepro.test === --- lldb/lit/Reproducer/TestFileRepro.test +++ lldb/lit/Reproducer/TestFileRepro.test @@ -7,9 +7,9 @@ # that the string "testing" is not printed. # RUN: %clang %S/Inputs/simple.c -g -o %t.out -# RUN: %lldb -x -b -s %S/Inputs/FileCapture.in --capture %t.repro -- %t.out | FileCheck %s --check-prefix CHECK --check-prefix CAPTURE +# RUN: %lldb -x -b -s %S/Inputs/FileCapture.in --capture %t.repro %t.out | FileCheck %s --check-prefix CHECK --check-prefix CAPTURE # RUN: rm %t.out -# RUN: %lldb -x -b -s %S/Inputs/FileReplay.in --replay %t.repro -- %t.out | FileCheck %s --check-prefix CHECK --check-prefix REPLAY +# RUN: %lldb --replay %t.repro | FileCheck %s --check-prefix CHECK --check-prefix REPLAY # CAPTURE: testing # REPLAY-NOT: testing Index: lldb/lit/Reproducer/Inputs/GDBRemoteReplay.in === --- lldb/lit/Reproducer/Inputs/GDBRemoteReplay.in +++ /dev/null @@ -1,5 +0,0 @@ -reproducer status -breakpoint set -f simple.c -l 12 -run -bt -cont Index: lldb/lit/Reproducer/Inputs/FileReplay.in === --- lldb/lit/Reproducer/Inputs/FileReplay.in +++ /dev/null @@ -1,2 +0,0 @@ -reproducer status -run Index: lldb/tools/driver/Driver.cpp === --- lldb/tools/driver/Driver.cpp +++ lldb/tools/driver/Driver.cpp @@ -905,7 +905,7 @@ WithColor::error() << "reproducer replay failed: " << error << '\n'; return 1; } -// FIXME: Return once SBReproducer::Replay actually performs the replay. +return 0; } SBError error = SBDebugger::InitializeWithErrorHandling(); Index: lldb/source/API/SBReproducer.cpp === --- lldb/source/API/SBReproducer.cpp +++ lldb/source/API/SBReproducer.cpp @@ -2918,8 +2918,6 @@ return error.c_str(); } - // FIXME: Enable the following code once the SB reproducer has landed. -#if 0 FileSpec file = loader->GetFile(); if (!file) { error = "unable to get replay data from reproducer."; @@ -2928,7 +2926,6 @@ SBRegistry registry; registry.Replay(file); -#endif return nullptr; } Index: lldb/lit/Reproducer/TestGDBRemoteRepro.test === --- lldb/lit/Reproducer/TestGDBRemoteRepro.test +++ lldb/lit/Reproducer/TestGDBRemoteRepro.test @@ -7,8 +7,8 @@ # that the string "testing"
[Lldb-commits] [PATCH] D58566: [Reproducers] Add more logging capabilities to reproducer instrumentation
JDevlieghere created this revision. JDevlieghere added reviewers: labath, davide, aprantl. JDevlieghere added a project: LLDB. Debugging issues with instrumentation capture and replay can be particularly tricky, especially because part of the process takes places even before the debugger is initialized. This patch adds more logging capabilities to these classes, hidden behind a macro define. Repository: rLLDB LLDB https://reviews.llvm.org/D58566 Files: lldb/include/lldb/Utility/ReproducerInstrumentation.h lldb/source/Utility/ReproducerInstrumentation.cpp Index: lldb/source/Utility/ReproducerInstrumentation.cpp === --- lldb/source/Utility/ReproducerInstrumentation.cpp +++ lldb/source/Utility/ReproducerInstrumentation.cpp @@ -44,22 +44,38 @@ bool Registry::Replay(llvm::StringRef buffer) { Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_API); - Deserializer deserializer(buffer); while (deserializer.HasData(1)) { unsigned id = deserializer.Deserialize(); -LLDB_LOG(log, "Replaying function #{0}", id); +#ifndef LLDB_REPRO_INSTR_TRACE +LLDB_LOG(log, "Replaying {0}: {1}", id, GetSignature(id)); +#else +(void)log; +llvm::errs() << "Replaying " << id << ": " << GetSignature(id) << "\n"; +#endif +assert(m_ids.count(id) != 0 && "Invalid id?"); m_ids[id]->operator()(deserializer); } return true; } -void Registry::DoRegister(uintptr_t RunID, std::unique_ptr replayer) { +unsigned Registry::DoRegister(uintptr_t RunID, + std::unique_ptr replayer) { const unsigned id = m_replayers.size() + 1; assert(m_replayers.find(RunID) == m_replayers.end()); m_replayers[RunID] = std::make_pair(std::move(replayer), id); m_ids[id] = m_replayers[RunID].first.get(); + return id; +} + +void Registry::RegisterSignature(unsigned id, llvm::StringRef result, + llvm::StringRef theclass, + llvm::StringRef method, + llvm::StringRef signature) { + m_signatures[id] = (result + (result.empty() ? "" : " ") + theclass + + "::" + method + signature) + .str(); } unsigned Registry::GetID(uintptr_t addr) { Index: lldb/include/lldb/Utility/ReproducerInstrumentation.h === --- lldb/include/lldb/Utility/ReproducerInstrumentation.h +++ lldb/include/lldb/Utility/ReproducerInstrumentation.h @@ -18,15 +18,37 @@ #include +// Define LLDB_REPRO_INSTR_TRACE to trace to stderr instead of LLDB's log +// infrastructure. This is useful when you need to see traces before the logger +// is initialized or enabled. +// #define LLDB_REPRO_INSTR_TRACE + #define LLDB_REGISTER_CONSTRUCTOR(Class, Signature)\ - Register(&construct::doit) + {\ +unsigned id = \ +Register(&construct::doit);\ +RegisterSignature(id, "", #Class, #Class, #Signature); \ + } + #define LLDB_REGISTER_METHOD(Result, Class, Method, Signature) \ - Register(&invoke::method<&Class::Method>::doit) + {\ +unsigned id = Register(\ +&invoke::method<&Class::Method>::doit);\ +RegisterSignature(id, #Result, #Class, #Method, #Signature); \ + } #define LLDB_REGISTER_METHOD_CONST(Result, Class, Method, Signature) \ - Register(&invoke::method_const<&Class::Method>::doit) + {\ +unsigned id = Register(\ +&invoke::method_const<&Class::Method>::doit); \ +RegisterSignature(id, #Result, #Class, #Method, #Signature); \ + } #define LLDB_REGISTER_STATIC_METHOD(Result, Class, Method, Signature) \ - Register(static_cast(&Class::Method)) + {\ +unsigned id = Register( \ +static_cast(&Class::Method)); \ +RegisterSignature(id, #Result, #Class, #Method, #Signature); \ + } #define LLDB_RECORD_CONSTRUCTOR(Class, Signature, ...) \ LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API), "{0}", \ @@ -224,6 +246,9 @@ /// Deserialize and interpret value as T. template T Deserialize() { +#ifdef LLDB_REPRO_INSTR_TRACE +llvm::errs() << "Deserializing with " << LLVM_PRETTY_FUNCTION << "\n"; +#endif return Read(typename serializer_tag::type()); } @@ -376,14 +401,16 @@ virtua