1 /*
2 * Copyright (C) 2024 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 "src/trace_processor/importers/perf_text/perf_text_trace_tokenizer.h"
18
19 #include <cctype>
20 #include <cstddef>
21 #include <cstdint>
22 #include <optional>
23 #include <string>
24 #include <string_view>
25 #include <utility>
26 #include <vector>
27
28 #include "perfetto/base/logging.h"
29 #include "perfetto/base/status.h"
30 #include "perfetto/ext/base/string_utils.h"
31 #include "perfetto/ext/base/string_view.h"
32 #include "perfetto/trace_processor/trace_blob_view.h"
33 #include "src/trace_processor/importers/common/mapping_tracker.h"
34 #include "src/trace_processor/importers/common/stack_profile_tracker.h"
35 #include "src/trace_processor/importers/common/virtual_memory_mapping.h"
36 #include "src/trace_processor/importers/perf_text/perf_text_event.h"
37 #include "src/trace_processor/importers/perf_text/perf_text_sample_line_parser.h"
38 #include "src/trace_processor/sorter/trace_sorter.h"
39 #include "src/trace_processor/storage/trace_storage.h"
40 #include "src/trace_processor/types/trace_processor_context.h"
41 #include "src/trace_processor/util/trace_blob_view_reader.h"
42
43 namespace perfetto::trace_processor::perf_text_importer {
44
45 namespace {
46
ToStringView(const TraceBlobView & tbv)47 std::string_view ToStringView(const TraceBlobView& tbv) {
48 return {reinterpret_cast<const char*>(tbv.data()), tbv.size()};
49 }
50
Slice(const std::string & str,size_t start,size_t end)51 std::string Slice(const std::string& str, size_t start, size_t end) {
52 return str.substr(start, end - start);
53 }
54
55 } // namespace
56
PerfTextTraceTokenizer(TraceProcessorContext * ctx)57 PerfTextTraceTokenizer::PerfTextTraceTokenizer(TraceProcessorContext* ctx)
58 : context_(ctx) {}
59 PerfTextTraceTokenizer::~PerfTextTraceTokenizer() = default;
60
Parse(TraceBlobView blob)61 base::Status PerfTextTraceTokenizer::Parse(TraceBlobView blob) {
62 reader_.PushBack(std::move(blob));
63 std::vector<FrameId> frames;
64 // Loop over each sample.
65 for (;;) {
66 auto it = reader_.GetIterator();
67 auto r = it.MaybeFindAndRead('\n');
68 if (!r) {
69 return base::OkStatus();
70 }
71 // The start line of a sample. An example:
72 // trace_processor 3962131 303057.417513: 1 cpu_atom/cycles/Pu:
73 //
74 // Note that perf script output is fully configurable so we have to be
75 // parse all the optionality carefully.
76 std::string_view first_line = ToStringView(*r);
77 std::optional<SampleLine> sample = ParseSampleLine(first_line);
78 if (!sample) {
79 return base::ErrStatus(
80 "Perf text parser: unable to parse sample line (context: '%s')",
81 std::string(first_line).c_str());
82 }
83
84 // Loop over the frames in the sample.
85 for (;;) {
86 auto raw_frame = it.MaybeFindAndRead('\n');
87 // If we don't manage to parse the full stack, we should bail out.
88 if (!raw_frame) {
89 return base::OkStatus();
90 }
91 // An empty line indicates that we have reached the end of this sample.
92 std::string frame =
93 base::TrimWhitespace(std::string(ToStringView(*raw_frame)));
94 if (frame.size() == 0) {
95 break;
96 }
97
98 size_t symbol_end = frame.find(' ');
99 if (symbol_end == std::string::npos) {
100 return base::ErrStatus(
101 "Perf text parser: unable to find symbol in frame (context: '%s')",
102 frame.c_str());
103 }
104
105 size_t mapping_start = frame.rfind('(');
106 if (mapping_start == std::string::npos || frame.back() != ')') {
107 return base::ErrStatus(
108 "Perf text parser: unable to find mapping in frame (context: '%s')",
109 frame.c_str());
110 }
111
112 std::string mapping_name =
113 Slice(frame, mapping_start + 1, frame.size() - 1);
114 DummyMemoryMapping* mapping;
115 if (DummyMemoryMapping** mapping_ptr = mappings_.Find(mapping_name);
116 mapping_ptr) {
117 mapping = *mapping_ptr;
118 } else {
119 mapping = &context_->mapping_tracker->CreateDummyMapping(mapping_name);
120 PERFETTO_CHECK(mappings_.Insert(mapping_name, mapping).second);
121 }
122
123 std::string symbol_name_with_offset =
124 base::TrimWhitespace(Slice(frame, symbol_end, mapping_start));
125 size_t offset = symbol_name_with_offset.rfind('+');
126 base::StringView symbol_name(symbol_name_with_offset);
127 if (offset != std::string::npos) {
128 symbol_name = symbol_name.substr(0, offset);
129 }
130 frames.emplace_back(
131 mapping->InternDummyFrame(symbol_name, base::StringView()));
132 }
133 if (frames.empty()) {
134 return base::ErrStatus(
135 "Perf text parser: no frames in sample (context: '%s')",
136 std::string(first_line).c_str());
137 }
138
139 std::optional<CallsiteId> parent_callsite;
140 uint32_t depth = 0;
141 for (auto rit = frames.rbegin(); rit != frames.rend(); ++rit) {
142 parent_callsite = context_->stack_profile_tracker->InternCallsite(
143 parent_callsite, *rit, ++depth);
144 }
145 frames.clear();
146
147 PerfTextEvent evt;
148 if (!sample->comm.empty()) {
149 evt.comm = context_->storage->InternString(
150 base::StringView(sample->comm.data(), sample->comm.size()));
151 }
152 evt.tid = sample->tid;
153 evt.pid = sample->pid;
154 evt.callsite_id = *parent_callsite;
155
156 context_->sorter->PushPerfTextEvent(sample->ts, evt);
157 reader_.PopFrontUntil(it.file_offset());
158 }
159 }
160
NotifyEndOfFile()161 base::Status PerfTextTraceTokenizer::NotifyEndOfFile() {
162 return base::OkStatus();
163 }
164
165 } // namespace perfetto::trace_processor::perf_text_importer
166