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