diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -2090,6 +2090,113 @@ } }; +static ThreadSP GetSingleThreadFromArgs(ExecutionContext &exe_ctx, Args &args, + CommandReturnObject &result) { + if (args.GetArgumentCount() == 0) + return exe_ctx.GetThreadSP(); + + const char *arg = args.GetArgumentAtIndex(0); + uint32_t thread_idx; + + if (!llvm::to_integer(arg, thread_idx)) { + result.AppendErrorWithFormat("invalid thread specification: \"%s\"\n", arg); + return nullptr; + } + ThreadSP thread_sp = + exe_ctx.GetProcessRef().GetThreadList().FindThreadByIndexID(thread_idx); + if (!thread_sp) + result.AppendErrorWithFormat("no thread with index: \"%s\"\n", arg); + return thread_sp; +} + +// CommandObjectTraceDumpFunctionCalls +#define LLDB_OPTIONS_thread_trace_dump_function_calls +#include "CommandOptions.inc" + +class CommandObjectTraceDumpFunctionCalls : public CommandObjectParsed { +public: + class CommandOptions : public Options { + public: + CommandOptions() { OptionParsingStarting(nullptr); } + + ~CommandOptions() override = default; + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) { + case 'j': { + m_json = true; + break; + } + case 'J': { + m_pretty_json = true; + break; + } + case 'F': { + m_output_file.emplace(option_arg); + break; + } + default: + llvm_unreachable("Unimplemented option"); + } + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_json = false; + m_pretty_json = false; + m_output_file = llvm::None; + } + + llvm::ArrayRef GetDefinitions() override { + return llvm::makeArrayRef(g_thread_trace_dump_function_calls_options); + } + + static const size_t kDefaultCount = 20; + + // Instance variables to hold the values for command options. + bool m_json; + bool m_pretty_json; + llvm::Optional m_output_file; + }; + + CommandObjectTraceDumpFunctionCalls(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "thread trace dump function-calls", + "Dump the traced function-calls for one thread. If no " + "thread is specified, the current thread is used.", + nullptr, + eCommandRequiresProcess | eCommandRequiresThread | + eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | + eCommandProcessMustBePaused | eCommandProcessMustBeTraced) { + CommandArgumentData thread_arg{eArgTypeThreadIndex, eArgRepeatOptional}; + m_arguments.push_back({thread_arg}); + } + + ~CommandObjectTraceDumpFunctionCalls() override = default; + + Options *GetOptions() override { return &m_options; } + +protected: + bool DoExecute(Args &args, CommandReturnObject &result) override { + ThreadSP thread_sp = GetSingleThreadFromArgs(m_exe_ctx, args, result); + if (!thread_sp) { + result.AppendError("invalid thread\n"); + return false; + } + result.AppendMessageWithFormatv( + "json = {0}, pretty_json = {1}, file = {2}, thread = {3}", + m_options.m_json, m_options.m_pretty_json, !!m_options.m_output_file, + thread_sp->GetID()); + return true; + } + + CommandOptions m_options; +}; + // CommandObjectTraceDumpInstructions #define LLDB_OPTIONS_thread_trace_dump_instructions #include "CommandOptions.inc" @@ -2238,28 +2345,8 @@ } protected: - ThreadSP GetThread(Args &args, CommandReturnObject &result) { - if (args.GetArgumentCount() == 0) - return m_exe_ctx.GetThreadSP(); - - const char *arg = args.GetArgumentAtIndex(0); - uint32_t thread_idx; - - if (!llvm::to_integer(arg, thread_idx)) { - result.AppendErrorWithFormat("invalid thread specification: \"%s\"\n", - arg); - return nullptr; - } - ThreadSP thread_sp = - m_exe_ctx.GetProcessRef().GetThreadList().FindThreadByIndexID( - thread_idx); - if (!thread_sp) - result.AppendErrorWithFormat("no thread with index: \"%s\"\n", arg); - return thread_sp; - } - bool DoExecute(Args &args, CommandReturnObject &result) override { - ThreadSP thread_sp = GetThread(args, result); + ThreadSP thread_sp = GetSingleThreadFromArgs(m_exe_ctx, args, result); if (!thread_sp) { result.AppendError("invalid thread\n"); return false; @@ -2401,6 +2488,9 @@ LoadSubCommand( "instructions", CommandObjectSP(new CommandObjectTraceDumpInstructions(interpreter))); + LoadSubCommand( + "function-calls", + CommandObjectSP(new CommandObjectTraceDumpFunctionCalls(interpreter))); LoadSubCommand( "info", CommandObjectSP(new CommandObjectTraceDumpInfo(interpreter))); } diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -1093,6 +1093,19 @@ Desc<"Display thread plans for unreported threads">; } +let Command = "thread trace dump function calls" in { + def thread_trace_dump_function_calls_file : Option<"file", "F">, Group<1>, + Arg<"Filename">, + Desc<"Dump the function calls to a file instead of the standard output.">; + def thread_trace_dump_function_calls_json: Option<"json", "j">, + Group<1>, + Desc<"Dump in simple JSON format.">; + def thread_trace_dump_function_calls_pretty_json: Option<"pretty-json", "J">, + Group<1>, + Desc<"Dump in JSON format but pretty printing the output for easier " + "readability.">; +} + let Command = "thread trace dump instructions" in { def thread_trace_dump_instructions_forwards: Option<"forwards", "f">, Group<1>, diff --git a/lldb/test/API/commands/trace/TestTraceDumpFunctionCalls.py b/lldb/test/API/commands/trace/TestTraceDumpFunctionCalls.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/commands/trace/TestTraceDumpFunctionCalls.py @@ -0,0 +1,17 @@ +from intelpt_testcase import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * + +class TestTraceDumpInfo(TraceIntelPTTestCaseBase): + def testDumpFunctionCalls(self): + self.expect("trace load -v " + + os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json")) + + self.expect("thread trace dump function-calls 2", + error=True, substrs=['error: no thread with index: "2"']) + + self.expect("thread trace dump function-calls 1 -j", + substrs=['json = true, pretty_json = false, file = false, thread = 3842849']) + + self.expect("thread trace dump function-calls 1 -F /tmp -J", + substrs=['false, pretty_json = true, file = true, thread = 3842849'])