xref: /aosp_15_r20/system/extras/memory_replay/MemoryTrace.cpp (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
1 /*
2  * Copyright (C) 2022 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 <inttypes.h>
18 #include <stdio.h>
19 #include <unistd.h>
20 
21 #include <string>
22 
23 #include <android-base/stringprintf.h>
24 
25 #include <memory_trace/MemoryTrace.h>
26 
27 namespace memory_trace {
28 
29 // This is larger than the maximum length of a possible line.
30 constexpr size_t kBufferLen = 256;
31 
FillInEntryFromString(const std::string & line,Entry & entry,std::string & error)32 bool FillInEntryFromString(const std::string& line, Entry& entry, std::string& error) {
33   // All lines have this format:
34   //   TID: ALLOCATION_TYPE POINTER [START_TIME_NS END_TIME_NS]
35   // where
36   //   TID is the thread id of the thread doing the operation.
37   //   ALLOCATION_TYPE is one of malloc, calloc, memalign, realloc, free, thread_done
38   //   POINTER is the hex value of the actual pointer
39   //   START_TIME_NS is the start time of the operation in nanoseconds.
40   //   END_TIME_NS is the end time of the operation in nanoseconds.
41   // The START_TIME_NS and END_TIME_NS are optional parameters, either both
42   // are present are neither are present.
43   int op_prefix_pos = 0;
44   char name[128];
45   if (sscanf(line.c_str(), "%d: %127s %" SCNx64 " %n", &entry.tid, name, &entry.ptr,
46              &op_prefix_pos) != 3) {
47     error = "Failed to process line: " + line;
48     return false;
49   }
50 
51   // Handle each individual type of entry type.
52   std::string type(name);
53   if (type == "thread_done") {
54     //   TID: thread_done 0x0 [END_TIME_NS]
55     // Where END_TIME_NS is optional.
56     entry.type = THREAD_DONE;
57     entry.start_ns = 0;
58     // Thread done has an optional time which is when the thread ended.
59     // This is the only entry type that has a single timestamp.
60     int n_match = sscanf(&line[op_prefix_pos], " %" SCNd64, &entry.end_ns);
61     entry.start_ns = 0;
62     if (n_match == EOF) {
63       entry.end_ns = 0;
64     } else if (n_match != 1) {
65       error = "Failed to read thread_done end time: " + line;
66       return false;
67     }
68     return true;
69   }
70 
71   int args_offset = 0;
72   const char* args_beg = &line[op_prefix_pos];
73   if (type == "malloc") {
74     // Format:
75     //   TID: malloc POINTER SIZE_OF_ALLOCATION [START_TIME_NS END_TIME_NS]
76     if (sscanf(args_beg, "%zu%n", &entry.size, &args_offset) != 1) {
77       error = "Failed to read malloc data: " + line;
78       return false;
79     }
80     entry.type = MALLOC;
81   } else if (type == "free") {
82     // Format:
83     //   TID: free POINTER [START_TIME_NS END_TIME_NS]
84     entry.type = FREE;
85   } else if (type == "calloc") {
86     // Format:
87     //   TID: calloc POINTER ITEM_COUNT ITEM_SIZE [START_TIME_NS END_TIME_NS]
88     if (sscanf(args_beg, "%" SCNd64 " %zu%n", &entry.u.n_elements, &entry.size, &args_offset) !=
89         2) {
90       error = "Failed to read calloc data: " + line;
91       return false;
92     }
93     entry.type = CALLOC;
94   } else if (type == "realloc") {
95     // Format:
96     //   TID: realloc POINTER OLD_POINTER NEW_SIZE [START_TIME_NS END_TIME_NS]
97     if (sscanf(args_beg, "%" SCNx64 " %zu%n", &entry.u.old_ptr, &entry.size, &args_offset) != 2) {
98       error = "Failed to read realloc data: " + line;
99       return false;
100     }
101     entry.type = REALLOC;
102   } else if (type == "memalign") {
103     // Format:
104     //   TID: memalign POINTER ALIGNMENT SIZE [START_TIME_NS END_TIME_NS]
105     if (sscanf(args_beg, "%" SCNd64 " %zu%n", &entry.u.align, &entry.size, &args_offset) != 2) {
106       error = "Failed to read memalign data: " + line;
107       return false;
108     }
109     entry.type = MEMALIGN;
110   } else {
111     printf("Unknown type %s: %s\n", type.c_str(), line.c_str());
112     error = "Unknown type " + type + ": " + line;
113     return false;
114   }
115 
116   const char* timestamps_beg = &args_beg[args_offset];
117 
118   // Get the optional timestamps if they exist.
119   int n_match = sscanf(timestamps_beg, "%" SCNd64 " %" SCNd64, &entry.start_ns, &entry.end_ns);
120   if (n_match == EOF) {
121     entry.start_ns = 0;
122     entry.end_ns = 0;
123   } else if (n_match != 2) {
124     error = "Failed to read timestamps: " + line;
125     return false;
126   }
127   return true;
128 }
129 
TypeToName(const TypeEnum type)130 static const char* TypeToName(const TypeEnum type) {
131   switch (type) {
132     case CALLOC:
133       return "calloc";
134     case FREE:
135       return "free";
136     case MALLOC:
137       return "malloc";
138     case MEMALIGN:
139       return "memalign";
140     case REALLOC:
141       return "realloc";
142     case THREAD_DONE:
143       return "thread_done";
144   }
145   return "unknown";
146 }
147 
FormatEntry(const Entry & entry,char * buffer,size_t buffer_len)148 static size_t FormatEntry(const Entry& entry, char* buffer, size_t buffer_len) {
149   int len = snprintf(buffer, buffer_len, "%d: %s 0x%" PRIx64, entry.tid, TypeToName(entry.type),
150                      entry.ptr);
151   if (len < 0) {
152     return 0;
153   }
154   size_t cur_len = len;
155   switch (entry.type) {
156     case FREE:
157       len = 0;
158       break;
159     case CALLOC:
160       len = snprintf(&buffer[cur_len], buffer_len - cur_len, " %" PRIu64 " %zu", entry.u.n_elements,
161                      entry.size);
162       break;
163     case MALLOC:
164       len = snprintf(&buffer[cur_len], buffer_len - cur_len, " %zu", entry.size);
165       break;
166     case MEMALIGN:
167       len = snprintf(&buffer[cur_len], buffer_len - cur_len, " %" PRIu64 " %zu", entry.u.align,
168                      entry.size);
169       break;
170     case REALLOC:
171       len = snprintf(&buffer[cur_len], buffer_len - cur_len, " 0x%" PRIx64 " %zu", entry.u.old_ptr,
172                      entry.size);
173       break;
174     case THREAD_DONE:
175       // Thread done only has a single optional timestamp, end_ns.
176       if (entry.end_ns != 0) {
177         len = snprintf(&buffer[cur_len], buffer_len - cur_len, " %" PRId64, entry.end_ns);
178         if (len < 0) {
179           return 0;
180         }
181         return cur_len + len;
182       }
183       return cur_len;
184     default:
185       return 0;
186   }
187   if (len < 0) {
188     return 0;
189   }
190 
191   cur_len += len;
192   if (entry.start_ns == 0) {
193     return cur_len;
194   }
195 
196   len = snprintf(&buffer[cur_len], buffer_len - cur_len, " %" PRIu64 " %" PRIu64, entry.start_ns,
197                  entry.end_ns);
198   if (len < 0) {
199     return 0;
200   }
201   return cur_len + len;
202 }
203 
CreateStringFromEntry(const Entry & entry)204 std::string CreateStringFromEntry(const Entry& entry) {
205   std::string line(kBufferLen, '\0');
206 
207   size_t size = FormatEntry(entry, line.data(), line.size());
208   if (size == 0) {
209     return "";
210   }
211   line.resize(size);
212   return line;
213 }
214 
WriteEntryToFd(int fd,const Entry & entry)215 bool WriteEntryToFd(int fd, const Entry& entry) {
216   char buffer[kBufferLen];
217   size_t size = FormatEntry(entry, buffer, sizeof(buffer));
218   if (size == 0 || size == sizeof(buffer)) {
219     return false;
220   }
221   buffer[size++] = '\n';
222   buffer[size] = '\0';
223   ssize_t bytes = TEMP_FAILURE_RETRY(write(fd, buffer, size));
224   if (bytes < 0 || static_cast<size_t>(bytes) != size) {
225     return false;
226   }
227   return true;
228 }
229 
230 }  // namespace memory_trace
231