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