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 "../common/ThreadPostMortemTrace.h" 12 #include "CommandObjectTraceStartIntelPT.h" 13 #include "DecodedThread.h" 14 #include "TraceIntelPTConstants.h" 15 #include "TraceIntelPTSessionFileParser.h" 16 #include "TraceIntelPTSessionSaver.h" 17 #include "lldb/Core/PluginManager.h" 18 #include "lldb/Target/Process.h" 19 #include "lldb/Target/Target.h" 20 #include "llvm/ADT/None.h" 21 22 using namespace lldb; 23 using namespace lldb_private; 24 using namespace lldb_private::trace_intel_pt; 25 using namespace llvm; 26 27 LLDB_PLUGIN_DEFINE(TraceIntelPT) 28 29 lldb::CommandObjectSP 30 TraceIntelPT::GetProcessTraceStartCommand(CommandInterpreter &interpreter) { 31 return CommandObjectSP( 32 new CommandObjectProcessTraceStartIntelPT(*this, interpreter)); 33 } 34 35 lldb::CommandObjectSP 36 TraceIntelPT::GetThreadTraceStartCommand(CommandInterpreter &interpreter) { 37 return CommandObjectSP( 38 new CommandObjectThreadTraceStartIntelPT(*this, interpreter)); 39 } 40 41 void TraceIntelPT::Initialize() { 42 PluginManager::RegisterPlugin(GetPluginNameStatic(), "Intel Processor Trace", 43 CreateInstanceForSessionFile, 44 CreateInstanceForLiveProcess, 45 TraceIntelPTSessionFileParser::GetSchema()); 46 } 47 48 void TraceIntelPT::Terminate() { 49 PluginManager::UnregisterPlugin(CreateInstanceForSessionFile); 50 } 51 52 StringRef TraceIntelPT::GetSchema() { 53 return TraceIntelPTSessionFileParser::GetSchema(); 54 } 55 56 void TraceIntelPT::Dump(Stream *s) const {} 57 58 llvm::Error TraceIntelPT::SaveLiveTraceToDisk(FileSpec directory) { 59 RefreshLiveProcessState(); 60 return TraceIntelPTSessionSaver().SaveToDisk(*this, directory); 61 } 62 63 Expected<TraceSP> TraceIntelPT::CreateInstanceForSessionFile( 64 const json::Value &trace_session_file, StringRef session_file_dir, 65 Debugger &debugger) { 66 return TraceIntelPTSessionFileParser(debugger, trace_session_file, 67 session_file_dir) 68 .Parse(); 69 } 70 71 Expected<TraceSP> TraceIntelPT::CreateInstanceForLiveProcess(Process &process) { 72 TraceSP instance(new TraceIntelPT(process)); 73 process.GetTarget().SetTrace(instance); 74 return instance; 75 } 76 77 TraceIntelPT::TraceIntelPT( 78 const pt_cpu &cpu_info, 79 const std::vector<ThreadPostMortemTraceSP> &traced_threads) 80 : m_cpu_info(cpu_info) { 81 for (const ThreadPostMortemTraceSP &thread : traced_threads) { 82 m_thread_decoders.emplace(thread->GetID(), 83 std::make_unique<ThreadDecoder>(thread, *this)); 84 SetPostMortemThreadDataFile(thread->GetID(), "threadTraceBuffer", 85 thread->GetTraceFile()); 86 } 87 } 88 89 DecodedThreadSP TraceIntelPT::Decode(Thread &thread) { 90 RefreshLiveProcessState(); 91 if (m_live_refresh_error.hasValue()) 92 return std::make_shared<DecodedThread>( 93 thread.shared_from_this(), 94 createStringError(inconvertibleErrorCode(), *m_live_refresh_error)); 95 96 auto it = m_thread_decoders.find(thread.GetID()); 97 if (it == m_thread_decoders.end()) 98 return std::make_shared<DecodedThread>( 99 thread.shared_from_this(), 100 createStringError(inconvertibleErrorCode(), "thread not traced")); 101 return it->second->Decode(); 102 } 103 104 lldb::TraceCursorUP TraceIntelPT::GetCursor(Thread &thread) { 105 return Decode(thread)->GetCursor(); 106 } 107 108 void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) { 109 lldb::tid_t tid = thread.GetID(); 110 s.Format("\nthread #{0}: tid = {1}", thread.GetIndexID(), thread.GetID()); 111 if (!IsTraced(tid)) { 112 s << ", not traced\n"; 113 return; 114 } 115 s << "\n"; 116 117 Expected<size_t> raw_size = GetRawTraceSize(thread); 118 if (!raw_size) { 119 s.Format(" {0}\n", toString(raw_size.takeError())); 120 return; 121 } 122 123 DecodedThreadSP decoded_trace_sp = Decode(thread); 124 size_t insn_len = decoded_trace_sp->GetInstructionsCount(); 125 size_t mem_used = decoded_trace_sp->CalculateApproximateMemoryUsage(); 126 127 s.Format(" Total number of instructions: {0}\n", insn_len); 128 129 s << "\n Memory usage:\n"; 130 s.Format(" Raw trace size: {0} KiB\n", *raw_size / 1024); 131 s.Format( 132 " Total approximate memory usage (excluding raw trace): {0:2} KiB\n", 133 (double)mem_used / 1024); 134 if (insn_len != 0) 135 s.Format(" Average memory usage per instruction (excluding raw trace): " 136 "{0:2} bytes\n", 137 (double)mem_used / insn_len); 138 139 s << "\n Timing:\n"; 140 GetTimer().ForThread(tid).ForEachTimedTask( 141 [&](const std::string &name, std::chrono::milliseconds duration) { 142 s.Format(" {0}: {1:2}s\n", name, duration.count() / 1000.0); 143 }); 144 145 const DecodedThread::EventsStats &events_stats = 146 decoded_trace_sp->GetEventsStats(); 147 s << "\n Events:\n"; 148 s.Format(" Number of instructions with events: {0}\n", 149 events_stats.total_instructions_with_events); 150 s.Format(" Number of individual events: {0}\n", events_stats.total_count); 151 for (const auto &event_to_count : events_stats.events_counts) { 152 s.Format(" {0}: {1}\n", 153 trace_event_utils::EventToDisplayString(event_to_count.first), 154 event_to_count.second); 155 } 156 157 s << "\n Errors:\n"; 158 const DecodedThread::LibiptErrorsStats &tsc_errors_stats = 159 decoded_trace_sp->GetTscErrorsStats(); 160 s.Format(" Number of TSC decoding errors: {0}\n", 161 tsc_errors_stats.total_count); 162 for (const auto &error_message_to_count : 163 tsc_errors_stats.libipt_errors_counts) { 164 s.Format(" {0}: {1}\n", error_message_to_count.first, 165 error_message_to_count.second); 166 } 167 } 168 169 llvm::Expected<size_t> TraceIntelPT::GetRawTraceSize(Thread &thread) { 170 size_t size; 171 auto callback = [&](llvm::ArrayRef<uint8_t> data) { 172 size = data.size(); 173 return Error::success(); 174 }; 175 if (Error err = OnThreadBufferRead(thread.GetID(), callback)) 176 return std::move(err); 177 178 return size; 179 } 180 181 Expected<pt_cpu> TraceIntelPT::GetCPUInfoForLiveProcess() { 182 Expected<std::vector<uint8_t>> cpu_info = GetLiveProcessBinaryData("cpuInfo"); 183 if (!cpu_info) 184 return cpu_info.takeError(); 185 186 int64_t cpu_family = -1; 187 int64_t model = -1; 188 int64_t stepping = -1; 189 std::string vendor_id; 190 191 StringRef rest(reinterpret_cast<const char *>(cpu_info->data()), 192 cpu_info->size()); 193 while (!rest.empty()) { 194 StringRef line; 195 std::tie(line, rest) = rest.split('\n'); 196 197 SmallVector<StringRef, 2> columns; 198 line.split(columns, StringRef(":"), -1, false); 199 200 if (columns.size() < 2) 201 continue; // continue searching 202 203 columns[1] = columns[1].trim(" "); 204 if (columns[0].contains("cpu family") && 205 columns[1].getAsInteger(10, cpu_family)) 206 continue; 207 208 else if (columns[0].contains("model") && columns[1].getAsInteger(10, model)) 209 continue; 210 211 else if (columns[0].contains("stepping") && 212 columns[1].getAsInteger(10, stepping)) 213 continue; 214 215 else if (columns[0].contains("vendor_id")) { 216 vendor_id = columns[1].str(); 217 if (!vendor_id.empty()) 218 continue; 219 } 220 221 if ((cpu_family != -1) && (model != -1) && (stepping != -1) && 222 (!vendor_id.empty())) { 223 return pt_cpu{vendor_id == "GenuineIntel" ? pcv_intel : pcv_unknown, 224 static_cast<uint16_t>(cpu_family), 225 static_cast<uint8_t>(model), 226 static_cast<uint8_t>(stepping)}; 227 } 228 } 229 return createStringError(inconvertibleErrorCode(), 230 "Failed parsing the target's /proc/cpuinfo file"); 231 } 232 233 Expected<pt_cpu> TraceIntelPT::GetCPUInfo() { 234 if (!m_cpu_info) { 235 if (llvm::Expected<pt_cpu> cpu_info = GetCPUInfoForLiveProcess()) 236 m_cpu_info = *cpu_info; 237 else 238 return cpu_info.takeError(); 239 } 240 return *m_cpu_info; 241 } 242 243 Process *TraceIntelPT::GetLiveProcess() { return m_live_process; } 244 245 void TraceIntelPT::DoRefreshLiveProcessState( 246 Expected<TraceGetStateResponse> state) { 247 m_thread_decoders.clear(); 248 249 if (!state) { 250 m_live_refresh_error = toString(state.takeError()); 251 return; 252 } 253 254 for (const TraceThreadState &thread_state : state->tracedThreads) { 255 ThreadSP thread_sp = 256 m_live_process->GetThreadList().FindThreadByID(thread_state.tid); 257 m_thread_decoders.emplace( 258 thread_state.tid, std::make_unique<ThreadDecoder>(thread_sp, *this)); 259 } 260 } 261 262 bool TraceIntelPT::IsTraced(lldb::tid_t tid) { 263 RefreshLiveProcessState(); 264 return m_thread_decoders.count(tid); 265 } 266 267 // The information here should match the description of the intel-pt section 268 // of the jLLDBTraceStart packet in the lldb/docs/lldb-gdb-remote.txt 269 // documentation file. Similarly, it should match the CLI help messages of the 270 // TraceIntelPTOptions.td file. 271 const char *TraceIntelPT::GetStartConfigurationHelp() { 272 return R"(Parameters: 273 274 Note: If a parameter is not specified, a default value will be used. 275 276 - int threadBufferSize (defaults to 4096 bytes): 277 [process and thread tracing] 278 Trace size in bytes per thread. It must be a power of 2 greater 279 than or equal to 4096 (2^12). The trace is circular keeping the 280 the most recent data. 281 282 - boolean enableTsc (default to false): 283 [process and thread tracing] 284 Whether to use enable TSC timestamps or not. This is supported on 285 all devices that support intel-pt. 286 287 - psbPeriod (defaults to null): 288 [process and thread tracing] 289 This value defines the period in which PSB packets will be generated. 290 A PSB packet is a synchronization packet that contains a TSC 291 timestamp and the current absolute instruction pointer. 292 293 This parameter can only be used if 294 295 /sys/bus/event_source/devices/intel_pt/caps/psb_cyc 296 297 is 1. Otherwise, the PSB period will be defined by the processor. 298 299 If supported, valid values for this period can be found in 300 301 /sys/bus/event_source/devices/intel_pt/caps/psb_periods 302 303 which contains a hexadecimal number, whose bits represent 304 valid values e.g. if bit 2 is set, then value 2 is valid. 305 306 The psb_period value is converted to the approximate number of 307 raw trace bytes between PSB packets as: 308 309 2 ^ (value + 11) 310 311 e.g. value 3 means 16KiB between PSB packets. Defaults to 0 if 312 supported. 313 314 - int processBufferSizeLimit (defaults to 500 MB): 315 [process tracing only] 316 Maximum total trace size per process in bytes. This limit applies 317 to the sum of the sizes of all thread traces of this process, 318 excluding the ones created explicitly with "thread tracing". 319 Whenever a thread is attempted to be traced due to this command 320 and the limit would be reached, the process is stopped with a 321 "processor trace" reason, so that the user can retrace the process 322 if needed.)"; 323 } 324 325 Error TraceIntelPT::Start(size_t thread_buffer_size, 326 size_t total_buffer_size_limit, bool enable_tsc, 327 Optional<size_t> psb_period) { 328 TraceIntelPTStartRequest request; 329 request.threadBufferSize = thread_buffer_size; 330 request.processBufferSizeLimit = total_buffer_size_limit; 331 request.enableTsc = enable_tsc; 332 request.psbPeriod = psb_period.map([](size_t val) { return (int64_t)val; }); 333 request.type = GetPluginName().str(); 334 return Trace::Start(toJSON(request)); 335 } 336 337 Error TraceIntelPT::Start(StructuredData::ObjectSP configuration) { 338 size_t thread_buffer_size = kDefaultThreadBufferSize; 339 size_t process_buffer_size_limit = kDefaultProcessBufferSizeLimit; 340 bool enable_tsc = kDefaultEnableTscValue; 341 Optional<size_t> psb_period = kDefaultPsbPeriod; 342 343 if (configuration) { 344 if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) { 345 dict->GetValueForKeyAsInteger("threadBufferSize", thread_buffer_size); 346 dict->GetValueForKeyAsInteger("processBufferSizeLimit", 347 process_buffer_size_limit); 348 dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc); 349 dict->GetValueForKeyAsInteger("psbPeriod", psb_period); 350 } else { 351 return createStringError(inconvertibleErrorCode(), 352 "configuration object is not a dictionary"); 353 } 354 } 355 356 return Start(thread_buffer_size, process_buffer_size_limit, enable_tsc, 357 psb_period); 358 } 359 360 llvm::Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids, 361 size_t thread_buffer_size, bool enable_tsc, 362 Optional<size_t> psb_period) { 363 TraceIntelPTStartRequest request; 364 request.threadBufferSize = thread_buffer_size; 365 request.enableTsc = enable_tsc; 366 request.psbPeriod = psb_period.map([](size_t val) { return (int64_t)val; }); 367 request.type = GetPluginName().str(); 368 request.tids.emplace(); 369 for (lldb::tid_t tid : tids) 370 request.tids->push_back(tid); 371 return Trace::Start(toJSON(request)); 372 } 373 374 Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids, 375 StructuredData::ObjectSP configuration) { 376 size_t thread_buffer_size = kDefaultThreadBufferSize; 377 bool enable_tsc = kDefaultEnableTscValue; 378 Optional<size_t> psb_period = kDefaultPsbPeriod; 379 380 if (configuration) { 381 if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) { 382 dict->GetValueForKeyAsInteger("threadBufferSize", thread_buffer_size); 383 dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc); 384 dict->GetValueForKeyAsInteger("psbPeriod", psb_period); 385 } else { 386 return createStringError(inconvertibleErrorCode(), 387 "configuration object is not a dictionary"); 388 } 389 } 390 391 return Start(tids, thread_buffer_size, enable_tsc, psb_period); 392 } 393 394 Error TraceIntelPT::OnThreadBufferRead(lldb::tid_t tid, 395 OnBinaryDataReadCallback callback) { 396 return OnThreadBinaryDataRead(tid, "threadTraceBuffer", callback); 397 } 398 399 TaskTimer &TraceIntelPT::GetTimer() { return m_task_timer; } 400