xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/perf_text/perf_text_trace_tokenizer.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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