1 //===-- IntelPTSingleBufferTrace.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 "IntelPTSingleBufferTrace.h" 10 #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" 11 #include "lldb/Utility/Status.h" 12 #include "lldb/Utility/StreamString.h" 13 #include "llvm/Support/Host.h" 14 #include "llvm/Support/MemoryBuffer.h" 15 #include <linux/perf_event.h> 16 #include <sstream> 17 #include <sys/syscall.h> 18 #include <unistd.h> 19 20 using namespace lldb; 21 using namespace lldb_private; 22 using namespace process_linux; 23 using namespace llvm; 24 25 const char kOSEventIntelPTTypeFile[] = 26 "/sys/bus/event_source/devices/intel_pt/type"; 27 28 const char kPSBPeriodCapFile[] = 29 "/sys/bus/event_source/devices/intel_pt/caps/psb_cyc"; 30 31 const char kPSBPeriodValidValuesFile[] = 32 "/sys/bus/event_source/devices/intel_pt/caps/psb_periods"; 33 34 const char kPSBPeriodBitOffsetFile[] = 35 "/sys/bus/event_source/devices/intel_pt/format/psb_period"; 36 37 const char kTSCBitOffsetFile[] = 38 "/sys/bus/event_source/devices/intel_pt/format/tsc"; 39 40 enum IntelPTConfigFileType { 41 Hex = 0, 42 // 0 or 1 43 ZeroOne, 44 Decimal, 45 // a bit index file always starts with the prefix config: following by an int, 46 // which represents the offset of the perf_event_attr.config value where to 47 // store a given configuration. 48 BitOffset 49 }; 50 51 static Expected<uint32_t> ReadIntelPTConfigFile(const char *file, 52 IntelPTConfigFileType type) { 53 ErrorOr<std::unique_ptr<MemoryBuffer>> stream = 54 MemoryBuffer::getFileAsStream(file); 55 56 if (!stream) 57 return createStringError(inconvertibleErrorCode(), 58 "Can't open the file '%s'", file); 59 60 uint32_t value = 0; 61 StringRef text_buffer = stream.get()->getBuffer(); 62 63 if (type == BitOffset) { 64 const char *prefix = "config:"; 65 if (!text_buffer.startswith(prefix)) 66 return createStringError(inconvertibleErrorCode(), 67 "The file '%s' contents doesn't start with '%s'", 68 file, prefix); 69 text_buffer = text_buffer.substr(strlen(prefix)); 70 } 71 72 auto getRadix = [&]() { 73 switch (type) { 74 case Hex: 75 return 16; 76 case ZeroOne: 77 case Decimal: 78 case BitOffset: 79 return 10; 80 } 81 llvm_unreachable("Fully covered switch above!"); 82 }; 83 84 auto createError = [&](const char *expected_value_message) { 85 return createStringError( 86 inconvertibleErrorCode(), 87 "The file '%s' has an invalid value. It should be %s.", file, 88 expected_value_message); 89 }; 90 91 if (text_buffer.trim().consumeInteger(getRadix(), value) || 92 (type == ZeroOne && value != 0 && value != 1)) { 93 switch (type) { 94 case Hex: 95 return createError("an unsigned hexadecimal int"); 96 case ZeroOne: 97 return createError("0 or 1"); 98 case Decimal: 99 case BitOffset: 100 return createError("an unsigned decimal int"); 101 } 102 } 103 return value; 104 } 105 106 /// Return the Linux perf event type for Intel PT. 107 Expected<uint32_t> process_linux::GetIntelPTOSEventType() { 108 return ReadIntelPTConfigFile(kOSEventIntelPTTypeFile, 109 IntelPTConfigFileType::Decimal); 110 } 111 112 static Error CheckPsbPeriod(size_t psb_period) { 113 Expected<uint32_t> cap = 114 ReadIntelPTConfigFile(kPSBPeriodCapFile, IntelPTConfigFileType::ZeroOne); 115 if (!cap) 116 return cap.takeError(); 117 if (*cap == 0) 118 return createStringError(inconvertibleErrorCode(), 119 "psb_period is unsupported in the system."); 120 121 Expected<uint32_t> valid_values = ReadIntelPTConfigFile( 122 kPSBPeriodValidValuesFile, IntelPTConfigFileType::Hex); 123 if (!valid_values) 124 return valid_values.takeError(); 125 126 if (valid_values.get() & (1 << psb_period)) 127 return Error::success(); 128 129 std::ostringstream error; 130 // 0 is always a valid value 131 error << "Invalid psb_period. Valid values are: 0"; 132 uint32_t mask = valid_values.get(); 133 while (mask) { 134 int index = __builtin_ctz(mask); 135 if (index > 0) 136 error << ", " << index; 137 // clear the lowest bit 138 mask &= mask - 1; 139 } 140 error << "."; 141 return createStringError(inconvertibleErrorCode(), error.str().c_str()); 142 } 143 144 #ifdef PERF_ATTR_SIZE_VER5 145 static Expected<uint64_t> 146 GeneratePerfEventConfigValue(bool enable_tsc, 147 std::optional<uint64_t> psb_period) { 148 uint64_t config = 0; 149 // tsc is always supported 150 if (enable_tsc) { 151 if (Expected<uint32_t> offset = ReadIntelPTConfigFile( 152 kTSCBitOffsetFile, IntelPTConfigFileType::BitOffset)) 153 config |= 1 << *offset; 154 else 155 return offset.takeError(); 156 } 157 if (psb_period) { 158 if (Error error = CheckPsbPeriod(*psb_period)) 159 return std::move(error); 160 161 if (Expected<uint32_t> offset = ReadIntelPTConfigFile( 162 kPSBPeriodBitOffsetFile, IntelPTConfigFileType::BitOffset)) 163 config |= *psb_period << *offset; 164 else 165 return offset.takeError(); 166 } 167 return config; 168 } 169 170 /// Create a \a perf_event_attr configured for 171 /// an IntelPT event. 172 /// 173 /// \return 174 /// A \a perf_event_attr if successful, 175 /// or an \a llvm::Error otherwise. 176 static Expected<perf_event_attr> 177 CreateIntelPTPerfEventConfiguration(bool enable_tsc, 178 std::optional<uint64_t> psb_period) { 179 perf_event_attr attr; 180 memset(&attr, 0, sizeof(attr)); 181 attr.size = sizeof(attr); 182 attr.exclude_kernel = 1; 183 attr.exclude_hv = 1; 184 attr.exclude_idle = 1; 185 186 if (Expected<uint64_t> config_value = 187 GeneratePerfEventConfigValue(enable_tsc, psb_period)) 188 attr.config = *config_value; 189 else 190 return config_value.takeError(); 191 192 if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType()) 193 attr.type = *intel_pt_type; 194 else 195 return intel_pt_type.takeError(); 196 197 return attr; 198 } 199 #endif 200 201 size_t IntelPTSingleBufferTrace::GetIptTraceSize() const { 202 return m_perf_event.GetAuxBuffer().size(); 203 } 204 205 Error IntelPTSingleBufferTrace::Pause() { 206 return m_perf_event.DisableWithIoctl(); 207 } 208 209 Error IntelPTSingleBufferTrace::Resume() { 210 return m_perf_event.EnableWithIoctl(); 211 } 212 213 Expected<std::vector<uint8_t>> IntelPTSingleBufferTrace::GetIptTrace() { 214 // Disable the perf event to force a flush out of the CPU's internal buffer. 215 // Besides, we can guarantee that the CPU won't override any data as we are 216 // reading the buffer. 217 // The Intel documentation says: 218 // 219 // Packets are first buffered internally and then written out 220 // asynchronously. To collect packet output for postprocessing, a collector 221 // needs first to ensure that all packet data has been flushed from internal 222 // buffers. Software can ensure this by stopping packet generation by 223 // clearing IA32_RTIT_CTL.TraceEn (see “Disabling Packet Generation” in 224 // Section 35.2.7.2). 225 // 226 // This is achieved by the PERF_EVENT_IOC_DISABLE ioctl request, as 227 // mentioned in the man page of perf_event_open. 228 return m_perf_event.GetReadOnlyAuxBuffer(); 229 } 230 231 Expected<IntelPTSingleBufferTrace> 232 IntelPTSingleBufferTrace::Start(const TraceIntelPTStartRequest &request, 233 std::optional<lldb::tid_t> tid, 234 std::optional<cpu_id_t> cpu_id, bool disabled, 235 std::optional<int> cgroup_fd) { 236 #ifndef PERF_ATTR_SIZE_VER5 237 return createStringError(inconvertibleErrorCode(), 238 "Intel PT Linux perf event not supported"); 239 #else 240 Log *log = GetLog(POSIXLog::Trace); 241 242 LLDB_LOG(log, "Will start tracing thread id {0} and cpu id {1}", tid, cpu_id); 243 244 if (__builtin_popcount(request.ipt_trace_size) != 1 || 245 request.ipt_trace_size < 4096) { 246 return createStringError( 247 inconvertibleErrorCode(), 248 "The intel pt trace size must be a power of 2 greater than or equal to " 249 "4096 (2^12) bytes. It was %" PRIu64 ".", 250 request.ipt_trace_size); 251 } 252 uint64_t page_size = getpagesize(); 253 uint64_t aux_buffer_numpages = static_cast<uint64_t>(llvm::PowerOf2Floor( 254 (request.ipt_trace_size + page_size - 1) / page_size)); 255 256 Expected<perf_event_attr> attr = CreateIntelPTPerfEventConfiguration( 257 request.enable_tsc, 258 llvm::transformOptional(request.psb_period, [](int value) { 259 return static_cast<uint64_t>(value); 260 })); 261 if (!attr) 262 return attr.takeError(); 263 attr->disabled = disabled; 264 265 LLDB_LOG(log, "Will create intel pt trace buffer of size {0}", 266 request.ipt_trace_size); 267 unsigned long flags = 0; 268 if (cgroup_fd) { 269 tid = *cgroup_fd; 270 flags |= PERF_FLAG_PID_CGROUP; 271 } 272 273 if (Expected<PerfEvent> perf_event = 274 PerfEvent::Init(*attr, tid, cpu_id, -1, flags)) { 275 if (Error mmap_err = perf_event->MmapMetadataAndBuffers( 276 /*num_data_pages=*/0, aux_buffer_numpages, 277 /*data_buffer_write=*/true)) { 278 return std::move(mmap_err); 279 } 280 return IntelPTSingleBufferTrace(std::move(*perf_event)); 281 } else { 282 return perf_event.takeError(); 283 } 284 #endif 285 } 286 287 const PerfEvent &IntelPTSingleBufferTrace::GetPerfEvent() const { 288 return m_perf_event; 289 } 290