1 //===-- TraceIntelPT.cpp --------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "TraceIntelPT.h" 10 11 #include "CommandObjectTraceStartIntelPT.h" 12 #include "TraceIntelPTSessionFileParser.h" 13 #include "lldb/Core/PluginManager.h" 14 #include "lldb/Target/Process.h" 15 #include "lldb/Target/Target.h" 16 #include "lldb/Target/ThreadPostMortemTrace.h" 17 18 using namespace lldb; 19 using namespace lldb_private; 20 using namespace lldb_private::trace_intel_pt; 21 using namespace llvm; 22 23 LLDB_PLUGIN_DEFINE(TraceIntelPT) 24 25 lldb::CommandObjectSP 26 TraceIntelPT::GetProcessTraceStartCommand(CommandInterpreter &interpreter) { 27 return CommandObjectSP( 28 new CommandObjectProcessTraceStartIntelPT(*this, interpreter)); 29 } 30 31 lldb::CommandObjectSP 32 TraceIntelPT::GetThreadTraceStartCommand(CommandInterpreter &interpreter) { 33 return CommandObjectSP( 34 new CommandObjectThreadTraceStartIntelPT(*this, interpreter)); 35 } 36 37 void TraceIntelPT::Initialize() { 38 PluginManager::RegisterPlugin(GetPluginNameStatic(), "Intel Processor Trace", 39 CreateInstanceForSessionFile, 40 CreateInstanceForLiveProcess, 41 TraceIntelPTSessionFileParser::GetSchema()); 42 } 43 44 void TraceIntelPT::Terminate() { 45 PluginManager::UnregisterPlugin(CreateInstanceForSessionFile); 46 } 47 48 ConstString TraceIntelPT::GetPluginNameStatic() { 49 static ConstString g_name("intel-pt"); 50 return g_name; 51 } 52 53 StringRef TraceIntelPT::GetSchema() { 54 return TraceIntelPTSessionFileParser::GetSchema(); 55 } 56 57 //------------------------------------------------------------------ 58 // PluginInterface protocol 59 //------------------------------------------------------------------ 60 61 ConstString TraceIntelPT::GetPluginName() { return GetPluginNameStatic(); } 62 63 uint32_t TraceIntelPT::GetPluginVersion() { return 1; } 64 65 void TraceIntelPT::Dump(Stream *s) const {} 66 67 Expected<TraceSP> TraceIntelPT::CreateInstanceForSessionFile( 68 const json::Value &trace_session_file, StringRef session_file_dir, 69 Debugger &debugger) { 70 return TraceIntelPTSessionFileParser(debugger, trace_session_file, 71 session_file_dir) 72 .Parse(); 73 } 74 75 Expected<TraceSP> TraceIntelPT::CreateInstanceForLiveProcess(Process &process) { 76 TraceSP instance(new TraceIntelPT(process)); 77 process.GetTarget().SetTrace(instance); 78 return instance; 79 } 80 81 TraceIntelPT::TraceIntelPT( 82 const pt_cpu &cpu_info, 83 const std::vector<ThreadPostMortemTraceSP> &traced_threads) 84 : m_cpu_info(cpu_info) { 85 for (const ThreadPostMortemTraceSP &thread : traced_threads) 86 m_thread_decoders.emplace( 87 thread.get(), std::make_unique<PostMortemThreadDecoder>(thread, *this)); 88 } 89 90 const DecodedThread *TraceIntelPT::Decode(Thread &thread) { 91 RefreshLiveProcessState(); 92 if (m_failed_live_threads_decoder.hasValue()) 93 return &*m_failed_live_threads_decoder; 94 95 auto it = m_thread_decoders.find(&thread); 96 if (it == m_thread_decoders.end()) 97 return nullptr; 98 return &it->second->Decode(); 99 } 100 101 size_t TraceIntelPT::GetCursorPosition(Thread &thread) { 102 const DecodedThread *decoded_thread = Decode(thread); 103 if (!decoded_thread) 104 return 0; 105 return decoded_thread->GetCursorPosition(); 106 } 107 108 void TraceIntelPT::TraverseInstructions( 109 Thread &thread, size_t position, TraceDirection direction, 110 std::function<bool(size_t index, Expected<lldb::addr_t> load_addr)> 111 callback) { 112 const DecodedThread *decoded_thread = Decode(thread); 113 if (!decoded_thread) 114 return; 115 116 ArrayRef<IntelPTInstruction> instructions = decoded_thread->GetInstructions(); 117 118 ssize_t delta = direction == TraceDirection::Forwards ? 1 : -1; 119 for (ssize_t i = position; i < (ssize_t)instructions.size() && i >= 0; 120 i += delta) 121 if (!callback(i, instructions[i].GetLoadAddress())) 122 break; 123 } 124 125 Optional<size_t> TraceIntelPT::GetInstructionCount(Thread &thread) { 126 if (const DecodedThread *decoded_thread = Decode(thread)) 127 return decoded_thread->GetInstructions().size(); 128 else 129 return None; 130 } 131 132 Expected<pt_cpu> TraceIntelPT::GetCPUInfoForLiveProcess() { 133 Expected<std::vector<uint8_t>> cpu_info = GetLiveProcessBinaryData("cpuInfo"); 134 if (!cpu_info) 135 return cpu_info.takeError(); 136 137 int64_t cpu_family = -1; 138 int64_t model = -1; 139 int64_t stepping = -1; 140 std::string vendor_id; 141 142 StringRef rest(reinterpret_cast<const char *>(cpu_info->data()), 143 cpu_info->size()); 144 while (!rest.empty()) { 145 StringRef line; 146 std::tie(line, rest) = rest.split('\n'); 147 148 SmallVector<StringRef, 2> columns; 149 line.split(columns, StringRef(":"), -1, false); 150 151 if (columns.size() < 2) 152 continue; // continue searching 153 154 columns[1] = columns[1].trim(" "); 155 if (columns[0].contains("cpu family") && 156 columns[1].getAsInteger(10, cpu_family)) 157 continue; 158 159 else if (columns[0].contains("model") && columns[1].getAsInteger(10, model)) 160 continue; 161 162 else if (columns[0].contains("stepping") && 163 columns[1].getAsInteger(10, stepping)) 164 continue; 165 166 else if (columns[0].contains("vendor_id")) { 167 vendor_id = columns[1].str(); 168 if (!vendor_id.empty()) 169 continue; 170 } 171 172 if ((cpu_family != -1) && (model != -1) && (stepping != -1) && 173 (!vendor_id.empty())) { 174 return pt_cpu{vendor_id == "GenuineIntel" ? pcv_intel : pcv_unknown, 175 static_cast<uint16_t>(cpu_family), 176 static_cast<uint8_t>(model), 177 static_cast<uint8_t>(stepping)}; 178 } 179 } 180 return createStringError(inconvertibleErrorCode(), 181 "Failed parsing the target's /proc/cpuinfo file"); 182 } 183 184 Expected<pt_cpu> TraceIntelPT::GetCPUInfo() { 185 if (!m_cpu_info) { 186 if (llvm::Expected<pt_cpu> cpu_info = GetCPUInfoForLiveProcess()) 187 m_cpu_info = *cpu_info; 188 else 189 return cpu_info.takeError(); 190 } 191 return *m_cpu_info; 192 } 193 194 void TraceIntelPT::DoRefreshLiveProcessState( 195 Expected<TraceGetStateResponse> state) { 196 m_thread_decoders.clear(); 197 198 if (!state) { 199 m_failed_live_threads_decoder = DecodedThread(state.takeError()); 200 return; 201 } 202 203 for (const TraceThreadState &thread_state : state->tracedThreads) { 204 Thread &thread = 205 *m_live_process->GetThreadList().FindThreadByID(thread_state.tid); 206 m_thread_decoders.emplace( 207 &thread, std::make_unique<LiveThreadDecoder>(thread, *this)); 208 } 209 } 210 211 bool TraceIntelPT::IsTraced(const Thread &thread) { 212 return m_thread_decoders.count(&thread); 213 } 214 215 Error TraceIntelPT::Start(size_t thread_buffer_size, 216 size_t total_buffer_size_limit) { 217 TraceIntelPTStartRequest request; 218 request.threadBufferSize = thread_buffer_size; 219 request.processBufferSizeLimit = total_buffer_size_limit; 220 request.type = GetPluginName().AsCString(); 221 return Trace::Start(toJSON(request)); 222 } 223 224 llvm::Error TraceIntelPT::Start(const std::vector<lldb::tid_t> &tids, 225 size_t thread_buffer_size) { 226 TraceIntelPTStartRequest request; 227 request.threadBufferSize = thread_buffer_size; 228 request.type = GetPluginName().AsCString(); 229 request.tids.emplace(); 230 for (lldb::tid_t tid : tids) 231 request.tids->push_back(tid); 232 return Trace::Start(toJSON(request)); 233 } 234 235 Expected<std::vector<uint8_t>> 236 TraceIntelPT::GetLiveThreadBuffer(lldb::tid_t tid) { 237 return Trace::GetLiveThreadBinaryData(tid, "threadTraceBuffer"); 238 } 239