1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "tracing.h"
18
19 #include <string.h>
20
21 #include <map>
22 #include <string>
23 #include <vector>
24
25 #include <android-base/file.h>
26 #include <android-base/logging.h>
27 #include <android-base/stringprintf.h>
28 #include <android-base/strings.h>
29
30 #include "perf_event.h"
31 #include "utils.h"
32
33 const char TRACING_INFO_MAGIC[10] = {23, 8, 68, 't', 'r',
34 'a', 'c', 'i', 'n', 'g'};
35
36 template <class T>
AppendData(std::vector<char> & data,const T & s)37 void AppendData(std::vector<char>& data, const T& s) {
38 const char* p = reinterpret_cast<const char*>(&s);
39 data.insert(data.end(), p, p + sizeof(T));
40 }
41
AppendData(std::vector<char> & data,const char * s)42 static void AppendData(std::vector<char>& data, const char* s) {
43 data.insert(data.end(), s, s + strlen(s) + 1);
44 }
45
46 template <>
AppendData(std::vector<char> & data,const std::string & s)47 void AppendData(std::vector<char>& data, const std::string& s) {
48 data.insert(data.end(), s.c_str(), s.c_str() + s.size() + 1);
49 }
50
51 template <>
MoveFromBinaryFormat(std::string & data,const char * & p)52 void MoveFromBinaryFormat(std::string& data, const char*& p) {
53 data.clear();
54 while (*p != '\0') {
55 data.push_back(*p++);
56 }
57 p++;
58 }
59
AppendFile(std::vector<char> & data,const std::string & file,uint32_t file_size_bytes=8)60 static void AppendFile(std::vector<char>& data, const std::string& file,
61 uint32_t file_size_bytes = 8) {
62 if (file_size_bytes == 8) {
63 uint64_t file_size = file.size();
64 AppendData(data, file_size);
65 } else if (file_size_bytes == 4) {
66 uint32_t file_size = file.size();
67 AppendData(data, file_size);
68 }
69 data.insert(data.end(), file.begin(), file.end());
70 }
71
DetachFile(const char * & p,std::string & file,uint32_t file_size_bytes=8)72 static void DetachFile(const char*& p, std::string& file,
73 uint32_t file_size_bytes = 8) {
74 uint64_t file_size = ConvertBytesToValue(p, file_size_bytes);
75 p += file_size_bytes;
76 file.clear();
77 file.insert(file.end(), p, p + file_size);
78 p += file_size;
79 }
80
81 struct TraceType {
82 std::string system;
83 std::string name;
84 };
85
86 class TracingFile {
87 public:
88 TracingFile();
89 bool RecordHeaderFiles();
90 void RecordFtraceFiles(const std::vector<TraceType>& trace_types);
91 bool RecordEventFiles(const std::vector<TraceType>& trace_types);
92 bool RecordKallsymsFile();
93 bool RecordPrintkFormatsFile();
94 std::vector<char> BinaryFormat() const;
95 void LoadFromBinary(const std::vector<char>& data);
96 void Dump(size_t indent) const;
97 std::vector<TracingFormat> LoadTracingFormatsFromEventFiles() const;
GetKallsymsFile() const98 const std::string& GetKallsymsFile() const { return kallsyms_file; }
GetPageSize() const99 uint32_t GetPageSize() const { return page_size; }
100
101 private:
102 char magic[10];
103 std::string version;
104 char endian;
105 uint8_t size_of_long;
106 uint32_t page_size;
107 std::string header_page_file;
108 std::string header_event_file;
109
110 std::vector<std::string> ftrace_format_files;
111 // pair of system, format_file_data.
112 std::vector<std::pair<std::string, std::string>> event_format_files;
113
114 std::string kallsyms_file;
115 std::string printk_formats_file;
116 };
117
TracingFile()118 TracingFile::TracingFile() {
119 memcpy(magic, TRACING_INFO_MAGIC, sizeof(TRACING_INFO_MAGIC));
120 version = "0.5";
121 endian = 0;
122 size_of_long = static_cast<int>(sizeof(long));
123 page_size = static_cast<uint32_t>(::GetPageSize());
124 }
125
RecordHeaderFiles()126 bool TracingFile::RecordHeaderFiles() {
127 if (!android::base::ReadFileToString(
128 "/sys/kernel/debug/tracing/events/header_page", &header_page_file)) {
129 PLOG(ERROR)
130 << "failed to read /sys/kernel/debug/tracing/events/header_page";
131 return false;
132 }
133 if (!android::base::ReadFileToString(
134 "/sys/kernel/debug/tracing/events/header_event",
135 &header_event_file)) {
136 PLOG(ERROR)
137 << "failed to read /sys/kernel/debug/tracing/events/header_event";
138 return false;
139 }
140 return true;
141 }
142
RecordFtraceFiles(const std::vector<TraceType> & trace_types)143 void TracingFile::RecordFtraceFiles(const std::vector<TraceType>& trace_types) {
144 for (const auto& type : trace_types) {
145 std::string format_path = android::base::StringPrintf(
146 "/sys/kernel/debug/tracing/events/ftrace/%s/format", type.name.c_str());
147 std::string format_data;
148 if (android::base::ReadFileToString(format_path, &format_data)) {
149 ftrace_format_files.push_back(std::move(format_data));
150 }
151 }
152 }
153
RecordEventFiles(const std::vector<TraceType> & trace_types)154 bool TracingFile::RecordEventFiles(const std::vector<TraceType>& trace_types) {
155 for (const auto& type : trace_types) {
156 std::string format_path = android::base::StringPrintf(
157 "/sys/kernel/debug/tracing/events/%s/%s/format", type.system.c_str(),
158 type.name.c_str());
159 std::string format_data;
160 if (!android::base::ReadFileToString(format_path, &format_data)) {
161 PLOG(ERROR) << "failed to read " << format_path;
162 return false;
163 }
164 event_format_files.push_back(
165 std::make_pair(type.system, std::move(format_data)));
166 }
167 return true;
168 }
169
RecordPrintkFormatsFile()170 bool TracingFile::RecordPrintkFormatsFile() {
171 if (!android::base::ReadFileToString(
172 "/sys/kernel/debug/tracing/printk_formats", &printk_formats_file)) {
173 PLOG(ERROR) << "failed to read /sys/kernel/debug/tracing/printk_formats";
174 return false;
175 }
176 return true;
177 }
178
BinaryFormat() const179 std::vector<char> TracingFile::BinaryFormat() const {
180 std::vector<char> ret;
181 ret.insert(ret.end(), magic, magic + sizeof(magic));
182 AppendData(ret, version);
183 ret.push_back(endian);
184 AppendData(ret, size_of_long);
185 AppendData(ret, page_size);
186 AppendData(ret, "header_page");
187 AppendFile(ret, header_page_file);
188 AppendData(ret, "header_event");
189 AppendFile(ret, header_event_file);
190 int count = static_cast<int>(ftrace_format_files.size());
191 AppendData(ret, count);
192 for (const auto& format : ftrace_format_files) {
193 AppendFile(ret, format);
194 }
195 count = static_cast<int>(event_format_files.size());
196 AppendData(ret, count);
197 for (const auto& pair : event_format_files) {
198 AppendData(ret, pair.first);
199 AppendData(ret, 1);
200 AppendFile(ret, pair.second);
201 }
202 AppendFile(ret, kallsyms_file, 4);
203 AppendFile(ret, printk_formats_file, 4);
204 return ret;
205 }
206
LoadFromBinary(const std::vector<char> & data)207 void TracingFile::LoadFromBinary(const std::vector<char>& data) {
208 const char* p = data.data();
209 const char* end = data.data() + data.size();
210 CHECK(memcmp(p, magic, sizeof(magic)) == 0);
211 p += sizeof(magic);
212 MoveFromBinaryFormat(version, p);
213 MoveFromBinaryFormat(endian, p);
214 MoveFromBinaryFormat(size_of_long, p);
215 MoveFromBinaryFormat(page_size, p);
216 std::string filename;
217 MoveFromBinaryFormat(filename, p);
218 CHECK_EQ(filename, "header_page");
219 DetachFile(p, header_page_file);
220 MoveFromBinaryFormat(filename, p);
221 CHECK_EQ(filename, "header_event");
222 DetachFile(p, header_event_file);
223 uint32_t count;
224 MoveFromBinaryFormat(count, p);
225 ftrace_format_files.resize(count);
226 for (uint32_t i = 0; i < count; ++i) {
227 DetachFile(p, ftrace_format_files[i]);
228 }
229 MoveFromBinaryFormat(count, p);
230 event_format_files.clear();
231 for (uint32_t i = 0; i < count; ++i) {
232 std::string system;
233 MoveFromBinaryFormat(system, p);
234 uint32_t count_in_system;
235 MoveFromBinaryFormat(count_in_system, p);
236 for (uint32_t i = 0; i < count_in_system; ++i) {
237 std::string format;
238 DetachFile(p, format);
239 event_format_files.push_back(std::make_pair(system, std::move(format)));
240 }
241 }
242 DetachFile(p, kallsyms_file, 4);
243 DetachFile(p, printk_formats_file, 4);
244 CHECK_EQ(p, end);
245 }
246
Dump(size_t indent) const247 void TracingFile::Dump(size_t indent) const {
248 PrintIndented(indent, "tracing data:\n");
249 PrintIndented(indent + 1, "magic: ");
250 for (size_t i = 0; i < 3u; ++i) {
251 printf("0x%x ", magic[i]);
252 }
253 for (size_t i = 3; i < sizeof(magic); ++i) {
254 printf("%c", magic[i]);
255 }
256 printf("\n");
257 PrintIndented(indent + 1, "version: %s\n", version.c_str());
258 PrintIndented(indent + 1, "endian: %d\n", endian);
259 PrintIndented(indent + 1, "header_page:\n%s\n\n", header_page_file.c_str());
260 PrintIndented(indent + 1, "header_event:\n%s\n\n", header_event_file.c_str());
261 for (size_t i = 0; i < ftrace_format_files.size(); ++i) {
262 PrintIndented(indent + 1, "ftrace format file %zu/%zu:\n%s\n\n", i + 1,
263 ftrace_format_files.size(), ftrace_format_files[i].c_str());
264 }
265 for (size_t i = 0; i < event_format_files.size(); ++i) {
266 PrintIndented(indent + 1, "event format file %zu/%zu %s:\n%s\n\n", i + 1,
267 event_format_files.size(),
268 event_format_files[i].first.c_str(),
269 event_format_files[i].second.c_str());
270 }
271 PrintIndented(indent + 1, "kallsyms:\n%s\n\n", kallsyms_file.c_str());
272 PrintIndented(indent + 1, "printk_formats:\n%s\n\n",
273 printk_formats_file.c_str());
274 }
275
276 enum class FormatParsingState {
277 READ_NAME,
278 READ_ID,
279 READ_FIELDS,
280 READ_PRINTFMT,
281 };
282
283 // Parse lines like: field:char comm[16]; offset:8; size:16; signed:1;
ParseTracingField(const std::string & s)284 static TracingField ParseTracingField(const std::string& s) {
285 TracingField field;
286 size_t start = 0;
287 std::string name;
288 std::string value;
289 for (size_t i = 0; i < s.size(); ++i) {
290 if (!isspace(s[i]) && (i == 0 || isspace(s[i - 1]))) {
291 start = i;
292 } else if (s[i] == ':') {
293 name = s.substr(start, i - start);
294 start = i + 1;
295 } else if (s[i] == ';') {
296 value = s.substr(start, i - start);
297 if (name == "field") {
298 size_t pos = value.find_first_of('[');
299 if (pos == std::string::npos) {
300 field.name = value;
301 field.elem_count = 1;
302 } else {
303 field.name = value.substr(0, pos);
304 field.elem_count =
305 static_cast<size_t>(strtoull(&value[pos + 1], nullptr, 10));
306 }
307 } else if (name == "offset") {
308 field.offset =
309 static_cast<size_t>(strtoull(value.c_str(), nullptr, 10));
310 } else if (name == "size") {
311 size_t size = static_cast<size_t>(strtoull(value.c_str(), nullptr, 10));
312 CHECK_EQ(size % field.elem_count, 0u);
313 field.elem_size = size / field.elem_count;
314 } else if (name == "signed") {
315 int is_signed = static_cast<int>(strtoull(value.c_str(), nullptr, 10));
316 field.is_signed = (is_signed == 1);
317 }
318 }
319 }
320 return field;
321 }
322
LoadTracingFormatsFromEventFiles() const323 std::vector<TracingFormat> TracingFile::LoadTracingFormatsFromEventFiles()
324 const {
325 std::vector<TracingFormat> formats;
326 for (const auto& pair : event_format_files) {
327 TracingFormat format;
328 format.system_name = pair.first;
329 std::vector<std::string> strs = android::base::Split(pair.second, "\n");
330 FormatParsingState state = FormatParsingState::READ_NAME;
331 for (const auto& s : strs) {
332 if (state == FormatParsingState::READ_NAME) {
333 size_t pos = s.find_first_of("name:");
334 if (pos != std::string::npos) {
335 format.name = android::base::Trim(s.substr(pos + strlen("name:")));
336 state = FormatParsingState::READ_ID;
337 }
338 } else if (state == FormatParsingState::READ_ID) {
339 size_t pos = s.find_first_of("ID:");
340 if (pos != std::string::npos) {
341 format.id =
342 strtoull(s.substr(pos + strlen("ID:")).c_str(), nullptr, 10);
343 state = FormatParsingState::READ_FIELDS;
344 }
345 } else if (state == FormatParsingState::READ_FIELDS) {
346 size_t pos = s.find_first_of("field:");
347 if (pos != std::string::npos) {
348 TracingField field = ParseTracingField(s);
349 format.fields.push_back(field);
350 }
351 }
352 }
353 formats.push_back(format);
354 }
355 return formats;
356 }
357
Tracing(const std::vector<char> & data)358 Tracing::Tracing(const std::vector<char>& data) {
359 tracing_file_ = new TracingFile;
360 tracing_file_->LoadFromBinary(data);
361 }
362
~Tracing()363 Tracing::~Tracing() { delete tracing_file_; }
364
Dump(size_t indent)365 void Tracing::Dump(size_t indent) { tracing_file_->Dump(indent); }
366
GetTracingFormatHavingId(uint64_t trace_event_id)367 TracingFormat Tracing::GetTracingFormatHavingId(uint64_t trace_event_id) {
368 if (tracing_formats_.empty()) {
369 tracing_formats_ = tracing_file_->LoadTracingFormatsFromEventFiles();
370 }
371 for (const auto& format : tracing_formats_) {
372 if (format.id == trace_event_id) {
373 return format;
374 }
375 }
376 LOG(FATAL) << "no tracing format for id " << trace_event_id;
377 return TracingFormat();
378 }
379
GetTracingEventNameHavingId(uint64_t trace_event_id)380 std::string Tracing::GetTracingEventNameHavingId(uint64_t trace_event_id) {
381 if (tracing_formats_.empty()) {
382 tracing_formats_ = tracing_file_->LoadTracingFormatsFromEventFiles();
383 }
384 for (const auto& format : tracing_formats_) {
385 if (format.id == trace_event_id) {
386 return android::base::StringPrintf("%s:%s", format.system_name.c_str(),
387 format.name.c_str());
388 }
389 }
390 return "";
391 }
392
GetKallsyms() const393 const std::string& Tracing::GetKallsyms() const {
394 return tracing_file_->GetKallsymsFile();
395 }
396
GetPageSize() const397 uint32_t Tracing::GetPageSize() const { return tracing_file_->GetPageSize(); }
398
GetTracingData(const std::vector<const EventType * > & event_types,std::vector<char> * data)399 bool GetTracingData(const std::vector<const EventType*>& event_types,
400 std::vector<char>* data) {
401 data->clear();
402 std::vector<TraceType> trace_types;
403 for (const auto& type : event_types) {
404 CHECK_EQ(PERF_TYPE_TRACEPOINT, type->type);
405 size_t pos = type->name.find(':');
406 TraceType trace_type;
407 trace_type.system = type->name.substr(0, pos);
408 trace_type.name = type->name.substr(pos + 1);
409 trace_types.push_back(trace_type);
410 }
411 TracingFile tracing_file;
412 if (!tracing_file.RecordHeaderFiles()) {
413 return false;
414 }
415 tracing_file.RecordFtraceFiles(trace_types);
416 if (!tracing_file.RecordEventFiles(trace_types)) {
417 return false;
418 }
419 // Don't record /proc/kallsyms here, as it will be contained in
420 // KernelSymbolRecord.
421 if (!tracing_file.RecordPrintkFormatsFile()) {
422 return false;
423 }
424 *data = tracing_file.BinaryFormat();
425 return true;
426 }
427