1 /*
2 * Copyright (C) 2019 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/profiling/symbolizer/symbolize_database.h"
18
19 #include <map>
20 #include <utility>
21 #include <vector>
22
23 #include "perfetto/base/logging.h"
24 #include "perfetto/ext/base/string_utils.h"
25 #include "perfetto/protozero/scattered_heap_buffer.h"
26 #include "perfetto/trace_processor/trace_processor.h"
27
28 #include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
29 #include "protos/perfetto/trace/trace.pbzero.h"
30 #include "protos/perfetto/trace/trace_packet.pbzero.h"
31
32 #include "src/trace_processor/util/stack_traces_util.h"
33
34 namespace perfetto {
35 namespace profiling {
36
37 namespace {
38 using trace_processor::Iterator;
39
40 constexpr const char* kQueryUnsymbolized =
41 "select spm.name, spm.build_id, spf.rel_pc, spm.load_bias "
42 "from stack_profile_frame spf "
43 "join stack_profile_mapping spm "
44 "on spf.mapping = spm.id "
45 "where spm.build_id != '' and spf.symbol_set_id IS NULL";
46
47 using NameAndBuildIdPair = std::pair<std::string, std::string>;
48
49 struct UnsymbolizedMapping {
50 std::string name;
51 std::string build_id;
52 uint64_t load_bias;
operator <perfetto::profiling::__anon97ddea430111::UnsymbolizedMapping53 bool operator<(const UnsymbolizedMapping& o) const {
54 return std::tie(name, build_id, load_bias) <
55 std::tie(o.name, o.build_id, o.load_bias);
56 }
57 };
58
FromHex(const char * str,size_t size)59 std::string FromHex(const char* str, size_t size) {
60 if (size % 2) {
61 PERFETTO_DFATAL_OR_ELOG("Failed to parse hex %s", str);
62 return "";
63 }
64 std::string result(size / 2, '\0');
65 for (size_t i = 0; i < size; i += 2) {
66 char hex_byte[3];
67 hex_byte[0] = str[i];
68 hex_byte[1] = str[i + 1];
69 hex_byte[2] = '\0';
70 char* end;
71 long int byte = strtol(hex_byte, &end, 16);
72 if (*end != '\0') {
73 PERFETTO_DFATAL_OR_ELOG("Failed to parse hex %s", str);
74 return "";
75 }
76 result[i / 2] = static_cast<char>(byte);
77 }
78 return result;
79 }
80
FromHex(const std::string & str)81 std::string FromHex(const std::string& str) {
82 return FromHex(str.c_str(), str.size());
83 }
84
GetUnsymbolizedFrames(trace_processor::TraceProcessor * tp,bool convert_build_id_to_bytes)85 std::map<UnsymbolizedMapping, std::vector<uint64_t>> GetUnsymbolizedFrames(
86 trace_processor::TraceProcessor* tp,
87 bool convert_build_id_to_bytes) {
88 std::map<UnsymbolizedMapping, std::vector<uint64_t>> res;
89 Iterator it = tp->ExecuteQuery(kQueryUnsymbolized);
90 while (it.Next()) {
91 int64_t load_bias = it.Get(3).AsLong();
92 PERFETTO_CHECK(load_bias >= 0);
93 std::string build_id;
94 // TODO(b/148109467): Remove workaround once all active Chrome versions
95 // write raw bytes instead of a string as build_id.
96 std::string raw_build_id = it.Get(1).AsString();
97 if (convert_build_id_to_bytes &&
98 !trace_processor::util::IsHexModuleId(base::StringView(raw_build_id))) {
99 build_id = FromHex(raw_build_id);
100 } else {
101 build_id = raw_build_id;
102 }
103 UnsymbolizedMapping unsymbolized_mapping{it.Get(0).AsString(), build_id,
104 static_cast<uint64_t>(load_bias)};
105 int64_t rel_pc = it.Get(2).AsLong();
106 res[unsymbolized_mapping].emplace_back(rel_pc);
107 }
108 if (!it.Status().ok()) {
109 PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
110 it.Status().message().c_str());
111 return {};
112 }
113 return res;
114 }
115 } // namespace
116
SymbolizeDatabase(trace_processor::TraceProcessor * tp,Symbolizer * symbolizer,std::function<void (const std::string &)> callback)117 void SymbolizeDatabase(trace_processor::TraceProcessor* tp,
118 Symbolizer* symbolizer,
119 std::function<void(const std::string&)> callback) {
120 PERFETTO_CHECK(symbolizer);
121 auto unsymbolized =
122 GetUnsymbolizedFrames(tp, symbolizer->BuildIdNeedsHexConversion());
123 for (auto it = unsymbolized.cbegin(); it != unsymbolized.cend(); ++it) {
124 const auto& unsymbolized_mapping = it->first;
125 const std::vector<uint64_t>& rel_pcs = it->second;
126 auto res = symbolizer->Symbolize(unsymbolized_mapping.name,
127 unsymbolized_mapping.build_id,
128 unsymbolized_mapping.load_bias, rel_pcs);
129 if (res.empty())
130 continue;
131
132 protozero::HeapBuffered<perfetto::protos::pbzero::Trace> trace;
133 auto* packet = trace->add_packet();
134 auto* module_symbols = packet->set_module_symbols();
135 module_symbols->set_path(unsymbolized_mapping.name);
136 module_symbols->set_build_id(unsymbolized_mapping.build_id);
137 PERFETTO_DCHECK(res.size() == rel_pcs.size());
138 for (size_t i = 0; i < res.size(); ++i) {
139 auto* address_symbols = module_symbols->add_address_symbols();
140 address_symbols->set_address(rel_pcs[i]);
141 for (const SymbolizedFrame& frame : res[i]) {
142 auto* line = address_symbols->add_lines();
143 line->set_function_name(frame.function_name);
144 line->set_source_file_name(frame.file_name);
145 line->set_line_number(frame.line);
146 }
147 }
148 callback(trace.SerializeAsString());
149 }
150 }
151
GetPerfettoBinaryPath()152 std::vector<std::string> GetPerfettoBinaryPath() {
153 const char* root = getenv("PERFETTO_BINARY_PATH");
154 if (root != nullptr)
155 return base::SplitString(root, ":");
156 return {};
157 }
158
159 } // namespace profiling
160 } // namespace perfetto
161