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