diff --git a/lldb/include/lldb/Target/Trace.h b/lldb/include/lldb/Target/Trace.h --- a/lldb/include/lldb/Target/Trace.h +++ b/lldb/include/lldb/Target/Trace.h @@ -496,6 +496,10 @@ DoRefreshLiveProcessState(TraceGetStateResponse state, llvm::StringRef json_response) = 0; + /// Return the list of processes traced by this instance. None of the returned + /// pointers are invalid. + std::vector GetTracedProcesses() const; + /// Method to be invoked by the plug-in to refresh the live process state. It /// will invoked DoRefreshLiveProcessState at some point, which should be /// implemented by the plug-in for custom state handling. diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h @@ -163,6 +163,15 @@ private: friend class TraceIntelPTSessionFileParser; + /// Create post-mortem threads associated with the processes traced by this + /// instance using the context switch traces. + /// + /// This does nothing if the threads already exist. + /// + /// \return + /// An \a llvm::Error in case of failures. + llvm::Error CreateThreadsFromContextSwitches(); + llvm::Expected GetCPUInfoForLiveProcess(); /// Postmortem trace constructor @@ -176,7 +185,7 @@ /// \param[in] trace_threads /// The threads traced in the live session. They must belong to the /// processes mentioned above. - TraceIntelPT(JSONTraceSession &session, + TraceIntelPT(JSONTraceSession &session, const FileSpec &session_file_dir, llvm::ArrayRef traced_processes, llvm::ArrayRef traced_threads); @@ -209,6 +218,8 @@ llvm::Optional m_tsc_conversion; }; +using TraceIntelPTSP = std::shared_ptr; + } // namespace trace_intel_pt } // namespace lldb_private diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp @@ -75,6 +75,7 @@ } TraceIntelPT::TraceIntelPT(JSONTraceSession &session, + const FileSpec &session_file_dir, ArrayRef traced_processes, ArrayRef traced_threads) : Trace(traced_processes, session.GetCoreIds()), @@ -92,11 +93,19 @@ std::vector cores; for (const JSONCore &core : *session.cores) { + FileSpec trace_buffer(core.trace_buffer); + if (trace_buffer.IsRelative()) + trace_buffer.PrependPathComponent(session_file_dir); + SetPostMortemCoreDataFile(core.core_id, IntelPTDataKinds::kTraceBuffer, - FileSpec(core.trace_buffer)); + trace_buffer); + + FileSpec context_switch(core.context_switch_trace); + if (context_switch.IsRelative()) + context_switch.PrependPathComponent(session_file_dir); SetPostMortemCoreDataFile(core.core_id, IntelPTDataKinds::kPerfContextSwitchTrace, - FileSpec(core.context_switch_trace)); + context_switch); cores.push_back(core.core_id); } @@ -473,3 +482,45 @@ } TaskTimer &TraceIntelPT::GetTimer() { return m_task_timer; } + +Error TraceIntelPT::CreateThreadsFromContextSwitches() { + DenseMap> pids_to_tids; + + for (core_id_t core_id : GetTracedCores()) { + Error err = OnCoreBinaryDataRead( + core_id, IntelPTDataKinds::kPerfContextSwitchTrace, + [&](ArrayRef data) -> Error { + Expected> executions = + DecodePerfContextSwitchTrace(data, core_id, *m_tsc_conversion); + if (!executions) + return executions.takeError(); + for (const ThreadContinuousExecution &execution : *executions) + pids_to_tids[execution.pid].insert(execution.tid); + return Error::success(); + }); + if (err) + return err; + } + + DenseMap processes; + for (Process *proc : GetTracedProcesses()) + processes.try_emplace(proc->GetID(), proc); + + for (const auto &pid_to_tids : pids_to_tids) { + lldb::pid_t pid = pid_to_tids.first; + auto it = processes.find(pid); + if (it == processes.end()) + continue; + + Process &process = *it->second; + ThreadList &thread_list = process.GetThreadList(); + + for (lldb::tid_t tid : pid_to_tids.second) { + if (!thread_list.FindThreadByID(tid)) { + thread_list.AddThread(std::make_shared( + process, tid, /*trace_file*/ None)); + } + } + } + return Error::success(); +} diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h @@ -52,7 +52,7 @@ /// errors, return a null pointer. llvm::Expected Parse(); - lldb::TraceSP + llvm::Expected CreateTraceIntelPTInstance(JSONTraceSession &session, std::vector &parsed_processes); @@ -64,6 +64,11 @@ lldb::ThreadPostMortemTraceSP ParseThread(lldb::ProcessSP &process_sp, const JSONThread &thread); + /// Create the corresponding Threads and Process objects given the JSON + /// process definition. + /// + /// \param[in] process + /// The JSON process definition llvm::Expected ParseProcess(const JSONProcess &process); llvm::Error ParseModule(lldb::TargetSP &target_sp, const JSONModule &module); @@ -82,6 +87,8 @@ llvm::Error CreateJSONError(llvm::json::Path::Root &root, const llvm::json::Value &value); + /// Create the corresponding Process, Thread and Module objects given this + /// session file. llvm::Expected> ParseSessionFile(const JSONTraceSession &session); diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp @@ -164,6 +164,10 @@ "triple"?: string, // Optional clang/llvm target triple. "threads": [ + // A list of known threads for the given process. When context switch + // data is provided, LLDB will automatically create threads for the + // this process whenever it finds new threads when traversing the + // context switches. { "tid": integer, "traceBuffer"?: string @@ -205,6 +209,10 @@ "timeShift": integer, "timeZero": integer, } + "dontCreateThreadsFromContextSwitches"?: boolean, + // If this is true, then the automatic creation of threads from context switch + // data is disabled, and thus only the threads provided in the "processes.threads" + // section will be created. } Notes: @@ -217,7 +225,7 @@ return schema; } -TraceSP TraceIntelPTSessionFileParser::CreateTraceIntelPTInstance( +Expected TraceIntelPTSessionFileParser::CreateTraceIntelPTInstance( JSONTraceSession &session, std::vector &parsed_processes) { std::vector threads; std::vector processes; @@ -227,10 +235,16 @@ parsed_process.threads.end()); } - TraceSP trace_instance(new TraceIntelPT(session, processes, threads)); + TraceIntelPTSP trace_instance(new TraceIntelPT( + session, FileSpec(m_session_file_dir), processes, threads)); for (const ParsedProcess &parsed_process : parsed_processes) parsed_process.target_sp->SetTrace(trace_instance); + if (session.cores) { + if (Error err = trace_instance->CreateThreadsFromContextSwitches()) + return std::move(err); + } + return trace_instance; } diff --git a/lldb/source/Target/Trace.cpp b/lldb/source/Target/Trace.cpp --- a/lldb/source/Target/Trace.cpp +++ b/lldb/source/Target/Trace.cpp @@ -449,3 +449,14 @@ return *m_cores; return {}; } + +std::vector Trace::GetTracedProcesses() const { + std::vector processes; + + for (Process *proc : m_postmortem_processes) + processes.push_back(proc); + + if (m_live_process) + processes.push_back(m_live_process); + return processes; +} diff --git a/lldb/test/API/commands/trace/TestTraceLoad.py b/lldb/test/API/commands/trace/TestTraceLoad.py --- a/lldb/test/API/commands/trace/TestTraceLoad.py +++ b/lldb/test/API/commands/trace/TestTraceLoad.py @@ -21,6 +21,18 @@ substrs=["67910: [tsc=0x008fb5211bfdf270] 0x0000000000400bd7 addl $0x1, -0x4(%rbp)", "m.out`bar() + 26 at multi_thread.cpp:20:6"]) + def testLoadMultiCoreTraceWithMissingThreads(self): + src_dir = self.getSourceDir() + trace_definition_file = os.path.join(src_dir, "intelpt-multi-core-trace", "trace_missing_threads.json") + self.expect("trace load -v " + trace_definition_file, substrs=["intel-pt"]) + self.expect("thread trace dump instructions 3 -t", + substrs=["19521: [tsc=0x008fb5211c143fd8] error: expected tracing enabled event", + "m.out`foo() + 65 at multi_thread.cpp:12:21", + "19520: [tsc=0x008fb5211bfbc69e] 0x0000000000400ba7 jg 0x400bb3"]) + self.expect("thread trace dump instructions 2 -t", + substrs=["67910: [tsc=0x008fb5211bfdf270] 0x0000000000400bd7 addl $0x1, -0x4(%rbp)", + "m.out`bar() + 26 at multi_thread.cpp:20:6"]) + def testLoadTrace(self): src_dir = self.getSourceDir() trace_definition_file = os.path.join(src_dir, "intelpt-trace", "trace.json") diff --git a/lldb/test/API/commands/trace/intelpt-multi-core-trace/trace_missing_threads.json b/lldb/test/API/commands/trace/intelpt-multi-core-trace/trace_missing_threads.json new file mode 100644 --- /dev/null +++ b/lldb/test/API/commands/trace/intelpt-multi-core-trace/trace_missing_threads.json @@ -0,0 +1,44 @@ +{ + "cores": [ + { + "contextSwitchTrace": "/tmp/trace8/cores/45.perf_context_switch_trace", + "coreId": 45, + "traceBuffer": "/tmp/trace8/cores/45.intelpt_trace" + }, + { + "contextSwitchTrace": "/tmp/trace8/cores/51.perf_context_switch_trace", + "coreId": 51, + "traceBuffer": "/tmp/trace8/cores/51.intelpt_trace" + } + ], + "cpuInfo": { + "family": 6, + "model": 85, + "stepping": 4, + "vendor": "GenuineIntel" + }, + "processes": [ + { + "modules": [ + { + "file": "modules/m.out", + "systemPath": "/tmp/m.out", + "loadAddress": 4194304, + "uuid": "AEFB0D59-233F-80FF-6D3C-4DED498534CF-11017B3B" + } + ], + "pid": 3497234, + "threads": [ + { + "tid": 3497234 + } + ] + } + ], + "tscPerfZeroConversion": { + "timeMult": 1076264588, + "timeShift": 31, + "timeZero": 18433473881008870804 + }, + "type": "intel-pt" +}