[lldb][trace] Add a basic function call dumpdump [1] - Add the command scaffolding

The command is thread trace dump function-calls and as minimum will
require printing to a file in json and non-json format

I added a test

Differential Revision: https://reviews.llvm.org/D135521
This commit is contained in:
Walter Erquinigo
2022-10-08 14:06:44 -07:00
parent b2a294bcf8
commit 566146c03b
3 changed files with 141 additions and 21 deletions

View File

@@ -2090,6 +2090,113 @@ public:
}
};
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<OptionDefinition> 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<FileSpec> 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 @@ public:
}
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 @@ public:
LoadSubCommand(
"instructions",
CommandObjectSP(new CommandObjectTraceDumpInstructions(interpreter)));
LoadSubCommand(
"function-calls",
CommandObjectSP(new CommandObjectTraceDumpFunctionCalls(interpreter)));
LoadSubCommand(
"info", CommandObjectSP(new CommandObjectTraceDumpInfo(interpreter)));
}

View File

@@ -1093,6 +1093,19 @@ let Command = "thread plan list" in {
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>,

View File

@@ -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'])