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( 83 thread->GetID(), 84 std::make_unique<PostMortemThreadDecoder>(thread, *this)); 85 } 86 87 DecodedThreadSP TraceIntelPT::Decode(Thread &thread) { 88 RefreshLiveProcessState(); 89 if (m_live_refresh_error.hasValue()) 90 return std::make_shared<DecodedThread>( 91 thread.shared_from_this(), 92 createStringError(inconvertibleErrorCode(), *m_live_refresh_error)); 93 94 auto it = m_thread_decoders.find(thread.GetID()); 95 if (it == m_thread_decoders.end()) 96 return std::make_shared<DecodedThread>( 97 thread.shared_from_this(), 98 createStringError(inconvertibleErrorCode(), "thread not traced")); 99 return it->second->Decode(); 100 } 101 102 lldb::TraceCursorUP TraceIntelPT::GetCursor(Thread &thread) { 103 return Decode(thread)->GetCursor(); 104 } 105 106 void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) { 107 Optional<size_t> raw_size = GetRawTraceSize(thread); 108 s.Format("\nthread #{0}: tid = {1}", thread.GetIndexID(), thread.GetID()); 109 if (!raw_size) { 110 s << ", not traced\n"; 111 return; 112 } 113 s << "\n"; 114 DecodedThreadSP decoded_trace_sp = Decode(thread); 115 size_t insn_len = decoded_trace_sp->GetInstructionsCount(); 116 size_t mem_used = decoded_trace_sp->CalculateApproximateMemoryUsage(); 117 118 s.Format(" Raw trace size: {0} KiB\n", *raw_size / 1024); 119 s.Format(" Total number of instructions: {0}\n", insn_len); 120 s.Format(" Total approximate memory usage: {0:2} KiB\n", 121 (double)mem_used / 1024); 122 if (insn_len != 0) 123 s.Format(" Average memory usage per instruction: {0:2} bytes\n", 124 (double)mem_used / insn_len); 125 126 const DecodedThread::LibiptErrors &tsc_errors = 127 decoded_trace_sp->GetTscErrors(); 128 s.Format("\n Number of TSC decoding errors: {0}\n", tsc_errors.total_count); 129 for (const auto &error_message_to_count : tsc_errors.libipt_errors) { 130 s.Format(" {0}: {1}\n", error_message_to_count.first, 131 error_message_to_count.second); 132 } 133 } 134 135 Optional<size_t> TraceIntelPT::GetRawTraceSize(Thread &thread) { 136 if (IsTraced(thread.GetID())) 137 return Decode(thread)->GetRawTraceSize(); 138 else 139 return None; 140 } 141 142 Expected<pt_cpu> TraceIntelPT::GetCPUInfoForLiveProcess() { 143 Expected<std::vector<uint8_t>> cpu_info = GetLiveProcessBinaryData("cpuInfo"); 144 if (!cpu_info) 145 return cpu_info.takeError(); 146 147 int64_t cpu_family = -1; 148 int64_t model = -1; 149 int64_t stepping = -1; 150 std::string vendor_id; 151 152 StringRef rest(reinterpret_cast<const char *>(cpu_info->data()), 153 cpu_info->size()); 154 while (!rest.empty()) { 155 StringRef line; 156 std::tie(line, rest) = rest.split('\n'); 157 158 SmallVector<StringRef, 2> columns; 159 line.split(columns, StringRef(":"), -1, false); 160 161 if (columns.size() < 2) 162 continue; // continue searching 163 164 columns[1] = columns[1].trim(" "); 165 if (columns[0].contains("cpu family") && 166 columns[1].getAsInteger(10, cpu_family)) 167 continue; 168 169 else if (columns[0].contains("model") && columns[1].getAsInteger(10, model)) 170 continue; 171 172 else if (columns[0].contains("stepping") && 173 columns[1].getAsInteger(10, stepping)) 174 continue; 175 176 else if (columns[0].contains("vendor_id")) { 177 vendor_id = columns[1].str(); 178 if (!vendor_id.empty()) 179 continue; 180 } 181 182 if ((cpu_family != -1) && (model != -1) && (stepping != -1) && 183 (!vendor_id.empty())) { 184 return pt_cpu{vendor_id == "GenuineIntel" ? pcv_intel : pcv_unknown, 185 static_cast<uint16_t>(cpu_family), 186 static_cast<uint8_t>(model), 187 static_cast<uint8_t>(stepping)}; 188 } 189 } 190 return createStringError(inconvertibleErrorCode(), 191 "Failed parsing the target's /proc/cpuinfo file"); 192 } 193 194 Expected<pt_cpu> TraceIntelPT::GetCPUInfo() { 195 if (!m_cpu_info) { 196 if (llvm::Expected<pt_cpu> cpu_info = GetCPUInfoForLiveProcess()) 197 m_cpu_info = *cpu_info; 198 else 199 return cpu_info.takeError(); 200 } 201 return *m_cpu_info; 202 } 203 204 Process *TraceIntelPT::GetLiveProcess() { return m_live_process; } 205 206 void TraceIntelPT::DoRefreshLiveProcessState( 207 Expected<TraceGetStateResponse> state) { 208 m_thread_decoders.clear(); 209 210 if (!state) { 211 m_live_refresh_error = toString(state.takeError()); 212 return; 213 } 214 215 for (const TraceThreadState &thread_state : state->tracedThreads) { 216 Thread &thread = 217 *m_live_process->GetThreadList().FindThreadByID(thread_state.tid); 218 m_thread_decoders.emplace( 219 thread_state.tid, std::make_unique<LiveThreadDecoder>(thread, *this)); 220 } 221 } 222 223 bool TraceIntelPT::IsTraced(lldb::tid_t tid) { 224 RefreshLiveProcessState(); 225 return m_thread_decoders.count(tid); 226 } 227 228 // The information here should match the description of the intel-pt section 229 // of the jLLDBTraceStart packet in the lldb/docs/lldb-gdb-remote.txt 230 // documentation file. Similarly, it should match the CLI help messages of the 231 // TraceIntelPTOptions.td file. 232 const char *TraceIntelPT::GetStartConfigurationHelp() { 233 return R"(Parameters: 234 235 Note: If a parameter is not specified, a default value will be used. 236 237 - int threadBufferSize (defaults to 4096 bytes): 238 [process and thread tracing] 239 Trace size in bytes per thread. It must be a power of 2 greater 240 than or equal to 4096 (2^12). The trace is circular keeping the 241 the most recent data. 242 243 - boolean enableTsc (default to false): 244 [process and thread tracing] 245 Whether to use enable TSC timestamps or not. This is supported on 246 all devices that support intel-pt. 247 248 - psbPeriod (defaults to null): 249 [process and thread tracing] 250 This value defines the period in which PSB packets will be generated. 251 A PSB packet is a synchronization packet that contains a TSC 252 timestamp and the current absolute instruction pointer. 253 254 This parameter can only be used if 255 256 /sys/bus/event_source/devices/intel_pt/caps/psb_cyc 257 258 is 1. Otherwise, the PSB period will be defined by the processor. 259 260 If supported, valid values for this period can be found in 261 262 /sys/bus/event_source/devices/intel_pt/caps/psb_periods 263 264 which contains a hexadecimal number, whose bits represent 265 valid values e.g. if bit 2 is set, then value 2 is valid. 266 267 The psb_period value is converted to the approximate number of 268 raw trace bytes between PSB packets as: 269 270 2 ^ (value + 11) 271 272 e.g. value 3 means 16KiB between PSB packets. Defaults to 0 if 273 supported. 274 275 - int processBufferSizeLimit (defaults to 500 MB): 276 [process tracing only] 277 Maximum total trace size per process in bytes. This limit applies 278 to the sum of the sizes of all thread traces of this process, 279 excluding the ones created explicitly with "thread tracing". 280 Whenever a thread is attempted to be traced due to this command 281 and the limit would be reached, the process is stopped with a 282 "processor trace" reason, so that the user can retrace the process 283 if needed.)"; 284 } 285 286 Error TraceIntelPT::Start(size_t thread_buffer_size, 287 size_t total_buffer_size_limit, bool enable_tsc, 288 Optional<size_t> psb_period) { 289 TraceIntelPTStartRequest request; 290 request.threadBufferSize = thread_buffer_size; 291 request.processBufferSizeLimit = total_buffer_size_limit; 292 request.enableTsc = enable_tsc; 293 request.psbPeriod = psb_period.map([](size_t val) { return (int64_t)val; }); 294 request.type = GetPluginName().str(); 295 return Trace::Start(toJSON(request)); 296 } 297 298 Error TraceIntelPT::Start(StructuredData::ObjectSP configuration) { 299 size_t thread_buffer_size = kDefaultThreadBufferSize; 300 size_t process_buffer_size_limit = kDefaultProcessBufferSizeLimit; 301 bool enable_tsc = kDefaultEnableTscValue; 302 Optional<size_t> psb_period = kDefaultPsbPeriod; 303 304 if (configuration) { 305 if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) { 306 dict->GetValueForKeyAsInteger("threadBufferSize", thread_buffer_size); 307 dict->GetValueForKeyAsInteger("processBufferSizeLimit", 308 process_buffer_size_limit); 309 dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc); 310 dict->GetValueForKeyAsInteger("psbPeriod", psb_period); 311 } else { 312 return createStringError(inconvertibleErrorCode(), 313 "configuration object is not a dictionary"); 314 } 315 } 316 317 return Start(thread_buffer_size, process_buffer_size_limit, enable_tsc, 318 psb_period); 319 } 320 321 llvm::Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids, 322 size_t thread_buffer_size, bool enable_tsc, 323 Optional<size_t> psb_period) { 324 TraceIntelPTStartRequest request; 325 request.threadBufferSize = thread_buffer_size; 326 request.enableTsc = enable_tsc; 327 request.psbPeriod = psb_period.map([](size_t val) { return (int64_t)val; }); 328 request.type = GetPluginName().str(); 329 request.tids.emplace(); 330 for (lldb::tid_t tid : tids) 331 request.tids->push_back(tid); 332 return Trace::Start(toJSON(request)); 333 } 334 335 Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids, 336 StructuredData::ObjectSP configuration) { 337 size_t thread_buffer_size = kDefaultThreadBufferSize; 338 bool enable_tsc = kDefaultEnableTscValue; 339 Optional<size_t> psb_period = kDefaultPsbPeriod; 340 341 if (configuration) { 342 if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) { 343 dict->GetValueForKeyAsInteger("threadBufferSize", thread_buffer_size); 344 dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc); 345 dict->GetValueForKeyAsInteger("psbPeriod", psb_period); 346 } else { 347 return createStringError(inconvertibleErrorCode(), 348 "configuration object is not a dictionary"); 349 } 350 } 351 352 return Start(tids, thread_buffer_size, enable_tsc, psb_period); 353 } 354 355 Expected<std::vector<uint8_t>> 356 TraceIntelPT::GetLiveThreadBuffer(lldb::tid_t tid) { 357 return Trace::GetLiveThreadBinaryData(tid, "threadTraceBuffer"); 358 } 359