• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&current_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(&current_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                     &current_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