• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   entry.u.old_ptr = 0;
52   entry.present_bytes = -1;
53   entry.start_ns = 0;
54   entry.end_ns = 0;
55 
56   // Handle each individual type of entry type.
57   std::string type(name);
58   if (type == "thread_done") {
59     //   TID: thread_done 0x0 [END_TIME_NS]
60     // Where END_TIME_NS is optional.
61     entry.type = THREAD_DONE;
62     entry.start_ns = 0;
63     // Thread done has an optional time which is when the thread ended.
64     // This is the only entry type that has a single timestamp.
65     int n_match = sscanf(&line[op_prefix_pos], " %" SCNd64, &entry.end_ns);
66     entry.start_ns = 0;
67     if (n_match == EOF) {
68       entry.end_ns = 0;
69     } else if (n_match != 1) {
70       error = "Failed to read thread_done end time: " + line;
71       return false;
72     }
73     return true;
74   }
75 
76   bool read_present_bytes = false;
77   int args_offset = 0;
78   const char* args = &line[op_prefix_pos];
79   if (type == "malloc") {
80     // Format:
81     //   TID: malloc POINTER SIZE_OF_ALLOCATION [START_TIME_NS END_TIME_NS]
82     if (sscanf(args, "%zu%n", &entry.size, &args_offset) != 1) {
83       error = "Failed to read malloc data: " + line;
84       return false;
85     }
86     entry.type = MALLOC;
87   } else if (type == "free") {
88     // Format:
89     //   TID: free POINTER [START_TIME_NS END_TIME_NS] [PRESENT_BYTES]
90     entry.type = FREE;
91     read_present_bytes = true;
92   } else if (type == "calloc") {
93     // Format:
94     //   TID: calloc POINTER ITEM_COUNT ITEM_SIZE [START_TIME_NS END_TIME_NS]
95     if (sscanf(args, "%" SCNd64 " %zu%n", &entry.u.n_elements, &entry.size, &args_offset) != 2) {
96       error = "Failed to read calloc data: " + line;
97       return false;
98     }
99     entry.type = CALLOC;
100   } else if (type == "realloc") {
101     // Format:
102     //   TID: realloc POINTER OLD_POINTER NEW_SIZE [START_TIME_NS END_TIME_NS] [PRESENT_BYTES]
103     if (sscanf(args, "%" SCNx64 " %zu%n", &entry.u.old_ptr, &entry.size, &args_offset) != 2) {
104       error = "Failed to read realloc data: " + line;
105       return false;
106     }
107     read_present_bytes = true;
108     entry.type = REALLOC;
109   } else if (type == "memalign") {
110     // Format:
111     //   TID: memalign POINTER ALIGNMENT SIZE [START_TIME_NS END_TIME_NS]
112     if (sscanf(args, "%" SCNd64 " %zu%n", &entry.u.align, &entry.size, &args_offset) != 2) {
113       error = "Failed to read memalign data: " + line;
114       return false;
115     }
116     entry.type = MEMALIGN;
117   } else {
118     error = "Unknown type " + type + ": " + line;
119     return false;
120   }
121 
122   // Get the optional timestamps if they exist.
123   args = &args[args_offset];
124   int n_match =
125       sscanf(args, "%" SCNd64 " %" SCNd64 "%n", &entry.start_ns, &entry.end_ns, &args_offset);
126   if (n_match == EOF) {
127     return true;
128   }
129 
130   if (n_match != 2) {
131     error = "Failed to read timestamps: " + line;
132     return false;
133   }
134 
135   // Get the optional present bytes if it exists.
136   if (read_present_bytes) {
137     n_match = sscanf(&args[args_offset], "%" SCNd64, &entry.present_bytes);
138     if (n_match != EOF && n_match != 1) {
139       error = "Failed to read present bytes: " + line;
140       return false;
141     }
142   }
143   return true;
144 }
145 
TypeToName(const TypeEnum type)146 static const char* TypeToName(const TypeEnum type) {
147   switch (type) {
148     case CALLOC:
149       return "calloc";
150     case FREE:
151       return "free";
152     case MALLOC:
153       return "malloc";
154     case MEMALIGN:
155       return "memalign";
156     case REALLOC:
157       return "realloc";
158     case THREAD_DONE:
159       return "thread_done";
160     default:
161       return "unknown";
162   }
163 }
164 
FormatEntry(const Entry & entry,char * buffer,size_t buffer_len)165 static size_t FormatEntry(const Entry& entry, char* buffer, size_t buffer_len) {
166   int len = snprintf(buffer, buffer_len, "%d: %s 0x%" PRIx64, entry.tid, TypeToName(entry.type),
167                      entry.ptr);
168   if (len < 0) {
169     return 0;
170   }
171   size_t cur_len = len;
172   bool output_present_bytes = false;
173   switch (entry.type) {
174     case FREE:
175       len = 0;
176       if (entry.present_bytes != -1) {
177         output_present_bytes = true;
178       }
179       break;
180     case CALLOC:
181       len = snprintf(&buffer[cur_len], buffer_len - cur_len, " %" PRIu64 " %zu", entry.u.n_elements,
182                      entry.size);
183       break;
184     case MALLOC:
185       len = snprintf(&buffer[cur_len], buffer_len - cur_len, " %zu", entry.size);
186       break;
187     case MEMALIGN:
188       len = snprintf(&buffer[cur_len], buffer_len - cur_len, " %" PRIu64 " %zu", entry.u.align,
189                      entry.size);
190       break;
191     case REALLOC:
192       len = snprintf(&buffer[cur_len], buffer_len - cur_len, " 0x%" PRIx64 " %zu", entry.u.old_ptr,
193                      entry.size);
194       if (entry.present_bytes != -1) {
195         output_present_bytes = true;
196       }
197       break;
198     case THREAD_DONE:
199       // Thread done only has a single optional timestamp, end_ns.
200       if (entry.end_ns != 0) {
201         len = snprintf(&buffer[cur_len], buffer_len - cur_len, " %" PRId64, entry.end_ns);
202         if (len < 0) {
203           return 0;
204         }
205         return cur_len + len;
206       }
207       return cur_len;
208     default:
209       return 0;
210   }
211   if (len < 0) {
212     return 0;
213   }
214 
215   cur_len += len;
216   if (entry.start_ns == 0 && !output_present_bytes) {
217     return cur_len;
218   }
219 
220   len = snprintf(&buffer[cur_len], buffer_len - cur_len, " %" PRIu64 " %" PRIu64, entry.start_ns,
221                  entry.end_ns);
222   if (len < 0) {
223     return 0;
224   }
225 
226   cur_len += len;
227   if (!output_present_bytes) {
228     return cur_len;
229   }
230 
231   len = snprintf(&buffer[cur_len], buffer_len - cur_len, " %" PRId64, entry.present_bytes);
232   if (len < 0) {
233     return 0;
234   }
235   return cur_len + len;
236 }
237 
CreateStringFromEntry(const Entry & entry)238 std::string CreateStringFromEntry(const Entry& entry) {
239   std::string line(kBufferLen, '\0');
240 
241   size_t size = FormatEntry(entry, line.data(), line.size());
242   if (size == 0) {
243     return "";
244   }
245   line.resize(size);
246   return line;
247 }
248 
WriteEntryToFd(int fd,const Entry & entry)249 bool WriteEntryToFd(int fd, const Entry& entry) {
250   char buffer[kBufferLen];
251   size_t size = FormatEntry(entry, buffer, sizeof(buffer));
252   if (size == 0 || size == sizeof(buffer)) {
253     return false;
254   }
255   buffer[size++] = '\n';
256   buffer[size] = '\0';
257   ssize_t bytes = TEMP_FAILURE_RETRY(write(fd, buffer, size));
258   if (bytes < 0 || static_cast<size_t>(bytes) != size) {
259     return false;
260   }
261   return true;
262 }
263 
264 }  // namespace memory_trace
265