1 /*
2 * Copyright (C) 2025 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 <stdio.h>
18
19 #include <map>
20 #include <memory>
21
22 #include "base/leb128.h"
23 #include "base/os.h"
24 #include "base/unix_file/fd_file.h"
25
26 namespace art {
27
28 // These constants are defined in the ART sources in the following files:
29 //
30 // - art/runtime/trace.h
31 // - art/runtime/trace_profile.cc
32 static const int kThreadInfoHeaderV2 = 0;
33 static const int kMethodInfoHeaderV2 = 1;
34 static const int kEntryHeaderV2 = 2;
35 static const int kMethodEntry = 0;
36 static const int kMethodExit = 1;
37 static const int kAlwaysOnMethodInfoHeaderSize = 11;
38 static const int kAlwaysOnTraceHeaderSize = 12;
39
ReadNumber(int num_bytes,uint8_t * header)40 uint64_t ReadNumber(int num_bytes, uint8_t* header) {
41 uint64_t number = 0;
42 for (int i = 0; i < num_bytes; i++) {
43 uint64_t c = header[i];
44 number += c << (i * 8);
45 }
46 return number;
47 }
48
ProcessMethodInfo(std::unique_ptr<File> & file,std::map<uint64_t,std::string> & name_map)49 bool ProcessMethodInfo(std::unique_ptr<File>& file, std::map<uint64_t, std::string>& name_map) {
50 // The first byte that specified the type of the packet is already read in
51 // ParseLongRunningMethodTrace.
52 uint8_t header[kAlwaysOnMethodInfoHeaderSize - 1];
53 if (!file->ReadFully(&header, sizeof(header))) {
54 printf("Couldn't read header\n");
55 return false;
56 }
57 uint64_t id = ReadNumber(8, header);
58 int length = ReadNumber(2, header + 8);
59
60 std::unique_ptr<char[]> name(new char[length]);
61 if (!file->ReadFully(name.get(), length)) {
62 return false;
63 }
64 std::string str(name.get(), length);
65 std::replace(str.begin(), str.end(), '\t', ' ');
66 if (str[str.length() - 1] == '\n') {
67 str.erase(str.length() - 1);
68 }
69 name_map.emplace(id, str);
70 return true;
71 }
72
PrintTraceEntry(const std::string & method_name,int event_type,int * current_depth,size_t timestamp)73 void PrintTraceEntry(const std::string& method_name,
74 int event_type,
75 int* current_depth,
76 size_t timestamp) {
77 std::string entry;
78 for (int i = 0; i < *current_depth; i++) {
79 entry.push_back('.');
80 }
81 if (event_type == kMethodEntry) {
82 *current_depth += 1;
83 entry.append(".>> ");
84 } else if (event_type == kMethodExit) {
85 *current_depth -= 1;
86 entry.append("<< ");
87 } else {
88 entry.append("?? ");
89 }
90 entry.append(" ");
91 entry.append(method_name);
92 entry.append(" ");
93 entry.append(std::to_string(timestamp));
94 entry.append("\n");
95 printf("%s", entry.c_str());
96 }
97
SkipTraceEntries(std::unique_ptr<File> & file)98 bool SkipTraceEntries(std::unique_ptr<File>& file) {
99 // The first byte that specified the type of the packet is already read in
100 // ParseLongRunningMethodTrace.
101 uint8_t header[kAlwaysOnTraceHeaderSize - 1];
102 if (!file->ReadFully(header, sizeof(header))) {
103 return false;
104 }
105
106 // Read thread id
107 ReadNumber(4, header);
108 int offset = 4;
109 // Read number of records
110 ReadNumber(3, header + offset);
111 offset += 3;
112 int total_size = ReadNumber(4, header + offset);
113 std::unique_ptr<uint8_t[]> buffer(new uint8_t[total_size]);
114 if (!file->ReadFully(buffer.get(), total_size)) {
115 return false;
116 }
117 return true;
118 }
119
ProcessLongRunningMethodTraceEntries(std::unique_ptr<File> & file,std::map<int64_t,int> & current_depth_map,std::map<uint64_t,std::string> & method_map)120 bool ProcessLongRunningMethodTraceEntries(std::unique_ptr<File>& file,
121 std::map<int64_t, int>& current_depth_map,
122 std::map<uint64_t, std::string>& method_map) {
123 // The first byte that specified the type of the packet is already read in
124 // ParseLongRunningMethodTrace.
125 uint8_t header[kAlwaysOnTraceHeaderSize - 1];
126 if (!file->ReadFully(header, sizeof(header))) {
127 return false;
128 }
129
130 uint32_t thread_id = ReadNumber(4, header);
131 int offset = 4;
132 int num_records = ReadNumber(3, header + offset);
133 offset += 3;
134 int total_size = ReadNumber(4, header + offset);
135 if (total_size == 0) {
136 return true;
137 }
138 std::unique_ptr<uint8_t[]> buffer(new uint8_t[total_size]);
139 if (!file->ReadFully(buffer.get(), total_size)) {
140 return false;
141 }
142
143 printf("Thread: %d\n", thread_id);
144 int current_depth = 0;
145 if (current_depth_map.find(thread_id) != current_depth_map.end()) {
146 // Get the current call stack depth. If it is the first method we are seeing on this thread
147 // then this map wouldn't have an entry, and we start with the depth of 0.
148 current_depth = current_depth_map[thread_id];
149 }
150
151 const uint8_t* current_buffer_ptr = buffer.get();
152 const uint8_t* end_ptr = buffer.get() + total_size;
153 uint64_t prev_method_id = 0;
154 int64_t prev_timestamp_and_action = 0;
155 for (int i = 0; i < num_records; i++) {
156 // Read timestamp and action
157 int64_t ts_diff = 0;
158 if (!DecodeSignedLeb128Checked(¤t_buffer_ptr, end_ptr, &ts_diff)) {
159 LOG(FATAL) << "Reading past the buffer when decoding timestamp";
160 }
161 int64_t timestamp_and_action = prev_timestamp_and_action + ts_diff;
162 prev_timestamp_and_action = timestamp_and_action;
163 bool is_method_exit = timestamp_and_action & 0x1;
164
165 uint64_t method_id;
166 std::string method_name;
167 if (!is_method_exit) {
168 int64_t method_diff = 0;
169 if (!DecodeSignedLeb128Checked(¤t_buffer_ptr, end_ptr, &method_diff)) {
170 LOG(FATAL) << "Reading past the buffer when decoding method id";
171 }
172 method_id = prev_method_id + method_diff;
173 prev_method_id = method_id;
174 if (method_map.find(method_id) == method_map.end()) {
175 LOG(FATAL) << "No entry for method " << std::hex << method_id;
176 }
177 method_name = method_map[method_id];
178 }
179
180 PrintTraceEntry(method_name,
181 is_method_exit? kMethodExit: kMethodEntry,
182 ¤t_depth,
183 timestamp_and_action & ~0x1);
184 }
185 current_depth_map[thread_id] = current_depth;
186 return true;
187 }
188
ParseLongRunningMethodTrace(char * file_name)189 void ParseLongRunningMethodTrace(char* file_name) {
190 std::unique_ptr<File> file(OS::OpenFileForReading(file_name));
191 if (file == nullptr) {
192 printf("Couldn't open file\n");
193 return;
194 }
195
196 // Map to maintain information about threads and methods
197 std::map<uint64_t, std::string> method_map;
198
199 // Map to Maintain the current depth of the method in the call stack. Used to
200 // correctly indent when printing the trace events.
201 std::map<int64_t, int> current_depth_map;
202
203 // First parse metadata. To keep the implementation of dumping the data
204 // simple, we don't ensure that the information about methods is dumped before the
205 // methods. This is also good if the ANR report got truncated. We will then
206 // have information about how long the methods took and we can infer some of
207 // the method names from the stack trace.
208 while (true) {
209 uint8_t entry_header;
210 if (!file->ReadFully(&entry_header, sizeof(entry_header))) {
211 break;
212 }
213 if (entry_header == kEntryHeaderV2) {
214 if (!SkipTraceEntries(file)) {
215 break;
216 }
217 } else {
218 DCHECK_EQ(entry_header, kMethodInfoHeaderV2);
219 if (!ProcessMethodInfo(file, method_map)) {
220 break;
221 }
222 }
223 }
224
225 // Reset file
226 file->ResetOffset();
227
228 while (true) {
229 uint8_t entry_header;
230 if (!file->ReadFully(&entry_header, sizeof(entry_header))) {
231 break;
232 }
233 if (entry_header != kEntryHeaderV2) {
234 break;
235 }
236 if (!ProcessLongRunningMethodTraceEntries(file, current_depth_map, method_map)) {
237 break;
238 }
239 }
240 }
241
242 } // namespace art
243
main(int argc,char ** argv)244 int main(int argc, char **argv) {
245 if (argc < 1) {
246 printf("Usage trace <filename>");
247 return -1;
248 }
249
250 art::ParseLongRunningMethodTrace(argv[1]);
251 return 0;
252 }
253