1*6dbdd20aSAndroid Build Coastguard Worker /*
2*6dbdd20aSAndroid Build Coastguard Worker * Copyright (C) 2021 The Android Open Source Project
3*6dbdd20aSAndroid Build Coastguard Worker *
4*6dbdd20aSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*6dbdd20aSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*6dbdd20aSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*6dbdd20aSAndroid Build Coastguard Worker *
8*6dbdd20aSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*6dbdd20aSAndroid Build Coastguard Worker *
10*6dbdd20aSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*6dbdd20aSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*6dbdd20aSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*6dbdd20aSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*6dbdd20aSAndroid Build Coastguard Worker * limitations under the License.
15*6dbdd20aSAndroid Build Coastguard Worker */
16*6dbdd20aSAndroid Build Coastguard Worker #include <memory>
17*6dbdd20aSAndroid Build Coastguard Worker
18*6dbdd20aSAndroid Build Coastguard Worker #include "src/profiling/symbolizer/breakpad_parser.h"
19*6dbdd20aSAndroid Build Coastguard Worker
20*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/base/logging.h"
21*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/file_utils.h"
22*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/string_splitter.h"
23*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/string_utils.h"
24*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/string_writer.h"
25*6dbdd20aSAndroid Build Coastguard Worker
26*6dbdd20aSAndroid Build Coastguard Worker namespace perfetto {
27*6dbdd20aSAndroid Build Coastguard Worker namespace profiling {
28*6dbdd20aSAndroid Build Coastguard Worker
29*6dbdd20aSAndroid Build Coastguard Worker namespace {
30*6dbdd20aSAndroid Build Coastguard Worker
SymbolComparator(const uint64_t i,const BreakpadParser::Symbol & sym)31*6dbdd20aSAndroid Build Coastguard Worker bool SymbolComparator(const uint64_t i, const BreakpadParser::Symbol& sym) {
32*6dbdd20aSAndroid Build Coastguard Worker return i < sym.start_address;
33*6dbdd20aSAndroid Build Coastguard Worker }
34*6dbdd20aSAndroid Build Coastguard Worker
GetFileContents(const std::string & file_path)35*6dbdd20aSAndroid Build Coastguard Worker std::optional<std::string> GetFileContents(const std::string& file_path) {
36*6dbdd20aSAndroid Build Coastguard Worker std::string file_contents;
37*6dbdd20aSAndroid Build Coastguard Worker base::ScopedFile fd = base::OpenFile(file_path, O_RDONLY);
38*6dbdd20aSAndroid Build Coastguard Worker // Read the contents of the file into |file_contents|.
39*6dbdd20aSAndroid Build Coastguard Worker if (!fd) {
40*6dbdd20aSAndroid Build Coastguard Worker return std::nullopt;
41*6dbdd20aSAndroid Build Coastguard Worker }
42*6dbdd20aSAndroid Build Coastguard Worker if (!base::ReadFileDescriptor(fd.get(), &file_contents)) {
43*6dbdd20aSAndroid Build Coastguard Worker return std::nullopt;
44*6dbdd20aSAndroid Build Coastguard Worker }
45*6dbdd20aSAndroid Build Coastguard Worker
46*6dbdd20aSAndroid Build Coastguard Worker return std::make_optional(std::move(file_contents));
47*6dbdd20aSAndroid Build Coastguard Worker }
48*6dbdd20aSAndroid Build Coastguard Worker
49*6dbdd20aSAndroid Build Coastguard Worker // Parses the given string and determines if it begins with the label
50*6dbdd20aSAndroid Build Coastguard Worker // 'MODULE'. Returns an ok status if it does begin with this label and a fail
51*6dbdd20aSAndroid Build Coastguard Worker // status otherwise.
ParseIfModuleRecord(base::StringView first_line)52*6dbdd20aSAndroid Build Coastguard Worker base::Status ParseIfModuleRecord(base::StringView first_line) {
53*6dbdd20aSAndroid Build Coastguard Worker // Split the given line by spaces.
54*6dbdd20aSAndroid Build Coastguard Worker const char kModuleLabel[] = "MODULE";
55*6dbdd20aSAndroid Build Coastguard Worker // Check to see if the line starts with 'MODULE'.
56*6dbdd20aSAndroid Build Coastguard Worker if (!base::StartsWith(first_line.ToStdString(), kModuleLabel)) {
57*6dbdd20aSAndroid Build Coastguard Worker return base::Status("Breakpad file not formatted correctly.");
58*6dbdd20aSAndroid Build Coastguard Worker }
59*6dbdd20aSAndroid Build Coastguard Worker return base::OkStatus();
60*6dbdd20aSAndroid Build Coastguard Worker }
61*6dbdd20aSAndroid Build Coastguard Worker
62*6dbdd20aSAndroid Build Coastguard Worker } // namespace
63*6dbdd20aSAndroid Build Coastguard Worker
BreakpadParser(const std::string & file_path)64*6dbdd20aSAndroid Build Coastguard Worker BreakpadParser::BreakpadParser(const std::string& file_path)
65*6dbdd20aSAndroid Build Coastguard Worker : file_path_(file_path) {}
66*6dbdd20aSAndroid Build Coastguard Worker
ParseFile()67*6dbdd20aSAndroid Build Coastguard Worker bool BreakpadParser::ParseFile() {
68*6dbdd20aSAndroid Build Coastguard Worker std::optional<std::string> file_contents = GetFileContents(file_path_);
69*6dbdd20aSAndroid Build Coastguard Worker if (!file_contents) {
70*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_ELOG("Could not get file contents of %s.", file_path_.c_str());
71*6dbdd20aSAndroid Build Coastguard Worker return false;
72*6dbdd20aSAndroid Build Coastguard Worker }
73*6dbdd20aSAndroid Build Coastguard Worker
74*6dbdd20aSAndroid Build Coastguard Worker // TODO(uwemwilson): Extract a build id and store it in the Symbol object.
75*6dbdd20aSAndroid Build Coastguard Worker
76*6dbdd20aSAndroid Build Coastguard Worker if (!ParseFromString(*file_contents)) {
77*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_ELOG("Could not parse file contents.");
78*6dbdd20aSAndroid Build Coastguard Worker return false;
79*6dbdd20aSAndroid Build Coastguard Worker }
80*6dbdd20aSAndroid Build Coastguard Worker
81*6dbdd20aSAndroid Build Coastguard Worker return true;
82*6dbdd20aSAndroid Build Coastguard Worker }
83*6dbdd20aSAndroid Build Coastguard Worker
ParseFromString(const std::string & file_contents)84*6dbdd20aSAndroid Build Coastguard Worker bool BreakpadParser::ParseFromString(const std::string& file_contents) {
85*6dbdd20aSAndroid Build Coastguard Worker // Create StringSplitter objects for each line so that specific lines can be
86*6dbdd20aSAndroid Build Coastguard Worker // used to create StringSplitter objects for words on that line.
87*6dbdd20aSAndroid Build Coastguard Worker base::StringSplitter lines(file_contents, '\n');
88*6dbdd20aSAndroid Build Coastguard Worker if (!lines.Next()) {
89*6dbdd20aSAndroid Build Coastguard Worker // File must be empty, so just return true and continue.
90*6dbdd20aSAndroid Build Coastguard Worker return true;
91*6dbdd20aSAndroid Build Coastguard Worker }
92*6dbdd20aSAndroid Build Coastguard Worker
93*6dbdd20aSAndroid Build Coastguard Worker // TODO(crbug/1239750): Extract a build id and store it in the Symbol object.
94*6dbdd20aSAndroid Build Coastguard Worker base::StringView first_line(lines.cur_token(), lines.cur_token_size());
95*6dbdd20aSAndroid Build Coastguard Worker base::Status parse_record_status = ParseIfModuleRecord(first_line);
96*6dbdd20aSAndroid Build Coastguard Worker if (!parse_record_status.ok()) {
97*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_ELOG("%s Breakpad files should begin with a MODULE record",
98*6dbdd20aSAndroid Build Coastguard Worker parse_record_status.message().c_str());
99*6dbdd20aSAndroid Build Coastguard Worker return false;
100*6dbdd20aSAndroid Build Coastguard Worker }
101*6dbdd20aSAndroid Build Coastguard Worker
102*6dbdd20aSAndroid Build Coastguard Worker // Parse each line.
103*6dbdd20aSAndroid Build Coastguard Worker while (lines.Next()) {
104*6dbdd20aSAndroid Build Coastguard Worker parse_record_status = ParseIfFuncRecord(lines.cur_token());
105*6dbdd20aSAndroid Build Coastguard Worker if (!parse_record_status.ok()) {
106*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_ELOG("%s", parse_record_status.message().c_str());
107*6dbdd20aSAndroid Build Coastguard Worker return false;
108*6dbdd20aSAndroid Build Coastguard Worker }
109*6dbdd20aSAndroid Build Coastguard Worker }
110*6dbdd20aSAndroid Build Coastguard Worker
111*6dbdd20aSAndroid Build Coastguard Worker return true;
112*6dbdd20aSAndroid Build Coastguard Worker }
113*6dbdd20aSAndroid Build Coastguard Worker
GetSymbol(uint64_t address) const114*6dbdd20aSAndroid Build Coastguard Worker std::optional<std::string> BreakpadParser::GetSymbol(uint64_t address) const {
115*6dbdd20aSAndroid Build Coastguard Worker // Returns an iterator pointing to the first element where the symbol's start
116*6dbdd20aSAndroid Build Coastguard Worker // address is greater than |address|.
117*6dbdd20aSAndroid Build Coastguard Worker auto it = std::upper_bound(symbols_.begin(), symbols_.end(), address,
118*6dbdd20aSAndroid Build Coastguard Worker &SymbolComparator);
119*6dbdd20aSAndroid Build Coastguard Worker // If the first symbol's address is greater than |address| then |address| is
120*6dbdd20aSAndroid Build Coastguard Worker // too low to appear in |symbols_|.
121*6dbdd20aSAndroid Build Coastguard Worker if (it == symbols_.begin()) {
122*6dbdd20aSAndroid Build Coastguard Worker return std::nullopt;
123*6dbdd20aSAndroid Build Coastguard Worker }
124*6dbdd20aSAndroid Build Coastguard Worker // upper_bound() returns the first symbol who's start address is greater than
125*6dbdd20aSAndroid Build Coastguard Worker // |address|. Therefore to find the symbol with a range of addresses that
126*6dbdd20aSAndroid Build Coastguard Worker // |address| falls into, we check the previous symbol.
127*6dbdd20aSAndroid Build Coastguard Worker it--;
128*6dbdd20aSAndroid Build Coastguard Worker // Check to see if the address is in the function's range.
129*6dbdd20aSAndroid Build Coastguard Worker if (address >= it->start_address &&
130*6dbdd20aSAndroid Build Coastguard Worker address < it->start_address + it->function_size) {
131*6dbdd20aSAndroid Build Coastguard Worker return it->symbol_name;
132*6dbdd20aSAndroid Build Coastguard Worker }
133*6dbdd20aSAndroid Build Coastguard Worker return std::nullopt;
134*6dbdd20aSAndroid Build Coastguard Worker }
135*6dbdd20aSAndroid Build Coastguard Worker
ParseIfFuncRecord(base::StringView current_line)136*6dbdd20aSAndroid Build Coastguard Worker base::Status BreakpadParser::ParseIfFuncRecord(base::StringView current_line) {
137*6dbdd20aSAndroid Build Coastguard Worker // Parses a FUNC record from a file. Structure of a FUNC record:
138*6dbdd20aSAndroid Build Coastguard Worker // FUNC [m] address size parameter_size name
139*6dbdd20aSAndroid Build Coastguard Worker // m: The m field is optional. If present it indicates that multiple symbols
140*6dbdd20aSAndroid Build Coastguard Worker // reference this function's instructions. (In which case, only one symbol
141*6dbdd20aSAndroid Build Coastguard Worker // name is mentioned within the breakpad file.)
142*6dbdd20aSAndroid Build Coastguard Worker // address: The start address of the function relative to the module's load
143*6dbdd20aSAndroid Build Coastguard Worker // address.
144*6dbdd20aSAndroid Build Coastguard Worker // size: The length in bytes of function's instructions.
145*6dbdd20aSAndroid Build Coastguard Worker // parameter_size: A hexadecimal number indicating the size, in bytes, of the
146*6dbdd20aSAndroid Build Coastguard Worker // arguments pushed on the stack for this function.
147*6dbdd20aSAndroid Build Coastguard Worker // name: The function name. This field may contain spaces.
148*6dbdd20aSAndroid Build Coastguard Worker // More info at
149*6dbdd20aSAndroid Build Coastguard Worker // https://chromium.googlesource.com/breakpad/breakpad/+/HEAD/docs/symbol_files.md
150*6dbdd20aSAndroid Build Coastguard Worker
151*6dbdd20aSAndroid Build Coastguard Worker const char kFuncLabel[] = "FUNC";
152*6dbdd20aSAndroid Build Coastguard Worker base::StringSplitter words(current_line.ToStdString(), ' ');
153*6dbdd20aSAndroid Build Coastguard Worker // Check to see if the first word indicates a FUNC record. If it is, create a
154*6dbdd20aSAndroid Build Coastguard Worker // Symbol struct and add tokens from words. If it isn't the function can just
155*6dbdd20aSAndroid Build Coastguard Worker // return true and resume parsing file.
156*6dbdd20aSAndroid Build Coastguard Worker if (!words.Next() || strcmp(words.cur_token(), kFuncLabel) != 0) {
157*6dbdd20aSAndroid Build Coastguard Worker return base::OkStatus();
158*6dbdd20aSAndroid Build Coastguard Worker }
159*6dbdd20aSAndroid Build Coastguard Worker
160*6dbdd20aSAndroid Build Coastguard Worker Symbol new_symbol;
161*6dbdd20aSAndroid Build Coastguard Worker // There can be either 4 or 5 FUNC record tokens. The second token, 'm' is
162*6dbdd20aSAndroid Build Coastguard Worker // optional.
163*6dbdd20aSAndroid Build Coastguard Worker const char kOptionalArg[] = "m";
164*6dbdd20aSAndroid Build Coastguard Worker // Get the first argument on the line.
165*6dbdd20aSAndroid Build Coastguard Worker words.Next();
166*6dbdd20aSAndroid Build Coastguard Worker
167*6dbdd20aSAndroid Build Coastguard Worker // If the optional argument is present, skip to the next token.
168*6dbdd20aSAndroid Build Coastguard Worker if (strcmp(words.cur_token(), kOptionalArg) == 0) {
169*6dbdd20aSAndroid Build Coastguard Worker words.Next();
170*6dbdd20aSAndroid Build Coastguard Worker }
171*6dbdd20aSAndroid Build Coastguard Worker
172*6dbdd20aSAndroid Build Coastguard Worker // Get the start address.
173*6dbdd20aSAndroid Build Coastguard Worker std::optional<uint64_t> optional_address =
174*6dbdd20aSAndroid Build Coastguard Worker base::CStringToUInt64(words.cur_token(), 16);
175*6dbdd20aSAndroid Build Coastguard Worker if (!optional_address) {
176*6dbdd20aSAndroid Build Coastguard Worker return base::Status("Address should be hexadecimal.");
177*6dbdd20aSAndroid Build Coastguard Worker }
178*6dbdd20aSAndroid Build Coastguard Worker new_symbol.start_address = *optional_address;
179*6dbdd20aSAndroid Build Coastguard Worker
180*6dbdd20aSAndroid Build Coastguard Worker // Get the function size.
181*6dbdd20aSAndroid Build Coastguard Worker words.Next();
182*6dbdd20aSAndroid Build Coastguard Worker std::optional<size_t> optional_func_size =
183*6dbdd20aSAndroid Build Coastguard Worker base::CStringToUInt32(words.cur_token(), 16);
184*6dbdd20aSAndroid Build Coastguard Worker if (!optional_func_size) {
185*6dbdd20aSAndroid Build Coastguard Worker return base::Status("Function size should be hexadecimal.");
186*6dbdd20aSAndroid Build Coastguard Worker }
187*6dbdd20aSAndroid Build Coastguard Worker new_symbol.function_size = *optional_func_size;
188*6dbdd20aSAndroid Build Coastguard Worker
189*6dbdd20aSAndroid Build Coastguard Worker // Skip the parameter size.
190*6dbdd20aSAndroid Build Coastguard Worker words.Next();
191*6dbdd20aSAndroid Build Coastguard Worker
192*6dbdd20aSAndroid Build Coastguard Worker // Get the function name. Function names can have spaces, so any token is now
193*6dbdd20aSAndroid Build Coastguard Worker // considered a part of the function name and will be appended to the buffer
194*6dbdd20aSAndroid Build Coastguard Worker // in |func_name_writer|.
195*6dbdd20aSAndroid Build Coastguard Worker std::unique_ptr<char[]> joined_string(new char[current_line.size()]);
196*6dbdd20aSAndroid Build Coastguard Worker base::StringWriter func_name_writer(joined_string.get(), current_line.size());
197*6dbdd20aSAndroid Build Coastguard Worker bool first_token = true;
198*6dbdd20aSAndroid Build Coastguard Worker while (words.Next()) {
199*6dbdd20aSAndroid Build Coastguard Worker if (!first_token) {
200*6dbdd20aSAndroid Build Coastguard Worker func_name_writer.AppendChar(' ');
201*6dbdd20aSAndroid Build Coastguard Worker } else {
202*6dbdd20aSAndroid Build Coastguard Worker first_token = false;
203*6dbdd20aSAndroid Build Coastguard Worker }
204*6dbdd20aSAndroid Build Coastguard Worker func_name_writer.AppendString(words.cur_token(), strlen(words.cur_token()));
205*6dbdd20aSAndroid Build Coastguard Worker }
206*6dbdd20aSAndroid Build Coastguard Worker
207*6dbdd20aSAndroid Build Coastguard Worker new_symbol.symbol_name = func_name_writer.GetStringView().ToStdString();
208*6dbdd20aSAndroid Build Coastguard Worker
209*6dbdd20aSAndroid Build Coastguard Worker symbols_.push_back(std::move(new_symbol));
210*6dbdd20aSAndroid Build Coastguard Worker
211*6dbdd20aSAndroid Build Coastguard Worker return base::OkStatus();
212*6dbdd20aSAndroid Build Coastguard Worker }
213*6dbdd20aSAndroid Build Coastguard Worker
214*6dbdd20aSAndroid Build Coastguard Worker } // namespace profiling
215*6dbdd20aSAndroid Build Coastguard Worker } // namespace perfetto
216