/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include namespace memory_trace { // This is larger than the maximum length of a possible line. constexpr size_t kBufferLen = 256; bool FillInEntryFromString(const std::string& line, Entry& entry, std::string& error) { // All lines have this format: // TID: ALLOCATION_TYPE POINTER [START_TIME_NS END_TIME_NS] // where // TID is the thread id of the thread doing the operation. // ALLOCATION_TYPE is one of malloc, calloc, memalign, realloc, free, thread_done // POINTER is the hex value of the actual pointer // START_TIME_NS is the start time of the operation in nanoseconds. // END_TIME_NS is the end time of the operation in nanoseconds. // The START_TIME_NS and END_TIME_NS are optional parameters, either both // are present are neither are present. int op_prefix_pos = 0; char name[128]; if (sscanf(line.c_str(), "%d: %127s %" SCNx64 " %n", &entry.tid, name, &entry.ptr, &op_prefix_pos) != 3) { error = "Failed to process line: " + line; return false; } // Handle each individual type of entry type. std::string type(name); if (type == "thread_done") { // TID: thread_done 0x0 [END_TIME_NS] // Where END_TIME_NS is optional. entry.type = THREAD_DONE; entry.start_ns = 0; // Thread done has an optional time which is when the thread ended. // This is the only entry type that has a single timestamp. int n_match = sscanf(&line[op_prefix_pos], " %" SCNd64, &entry.end_ns); entry.start_ns = 0; if (n_match == EOF) { entry.end_ns = 0; } else if (n_match != 1) { error = "Failed to read thread_done end time: " + line; return false; } return true; } int args_offset = 0; const char* args_beg = &line[op_prefix_pos]; if (type == "malloc") { // Format: // TID: malloc POINTER SIZE_OF_ALLOCATION [START_TIME_NS END_TIME_NS] if (sscanf(args_beg, "%zu%n", &entry.size, &args_offset) != 1) { error = "Failed to read malloc data: " + line; return false; } entry.type = MALLOC; } else if (type == "free") { // Format: // TID: free POINTER [START_TIME_NS END_TIME_NS] entry.type = FREE; } else if (type == "calloc") { // Format: // TID: calloc POINTER ITEM_COUNT ITEM_SIZE [START_TIME_NS END_TIME_NS] if (sscanf(args_beg, "%" SCNd64 " %zu%n", &entry.u.n_elements, &entry.size, &args_offset) != 2) { error = "Failed to read calloc data: " + line; return false; } entry.type = CALLOC; } else if (type == "realloc") { // Format: // TID: realloc POINTER OLD_POINTER NEW_SIZE [START_TIME_NS END_TIME_NS] if (sscanf(args_beg, "%" SCNx64 " %zu%n", &entry.u.old_ptr, &entry.size, &args_offset) != 2) { error = "Failed to read realloc data: " + line; return false; } entry.type = REALLOC; } else if (type == "memalign") { // Format: // TID: memalign POINTER ALIGNMENT SIZE [START_TIME_NS END_TIME_NS] if (sscanf(args_beg, "%" SCNd64 " %zu%n", &entry.u.align, &entry.size, &args_offset) != 2) { error = "Failed to read memalign data: " + line; return false; } entry.type = MEMALIGN; } else { printf("Unknown type %s: %s\n", type.c_str(), line.c_str()); error = "Unknown type " + type + ": " + line; return false; } const char* timestamps_beg = &args_beg[args_offset]; // Get the optional timestamps if they exist. int n_match = sscanf(timestamps_beg, "%" SCNd64 " %" SCNd64, &entry.start_ns, &entry.end_ns); if (n_match == EOF) { entry.start_ns = 0; entry.end_ns = 0; } else if (n_match != 2) { error = "Failed to read timestamps: " + line; return false; } return true; } static const char* TypeToName(const TypeEnum type) { switch (type) { case CALLOC: return "calloc"; case FREE: return "free"; case MALLOC: return "malloc"; case MEMALIGN: return "memalign"; case REALLOC: return "realloc"; case THREAD_DONE: return "thread_done"; } return "unknown"; } static size_t FormatEntry(const Entry& entry, char* buffer, size_t buffer_len) { int len = snprintf(buffer, buffer_len, "%d: %s 0x%" PRIx64, entry.tid, TypeToName(entry.type), entry.ptr); if (len < 0) { return 0; } size_t cur_len = len; switch (entry.type) { case FREE: len = 0; break; case CALLOC: len = snprintf(&buffer[cur_len], buffer_len - cur_len, " %" PRIu64 " %zu", entry.u.n_elements, entry.size); break; case MALLOC: len = snprintf(&buffer[cur_len], buffer_len - cur_len, " %zu", entry.size); break; case MEMALIGN: len = snprintf(&buffer[cur_len], buffer_len - cur_len, " %" PRIu64 " %zu", entry.u.align, entry.size); break; case REALLOC: len = snprintf(&buffer[cur_len], buffer_len - cur_len, " 0x%" PRIx64 " %zu", entry.u.old_ptr, entry.size); break; case THREAD_DONE: // Thread done only has a single optional timestamp, end_ns. if (entry.end_ns != 0) { len = snprintf(&buffer[cur_len], buffer_len - cur_len, " %" PRId64, entry.end_ns); if (len < 0) { return 0; } return cur_len + len; } return cur_len; default: return 0; } if (len < 0) { return 0; } cur_len += len; if (entry.start_ns == 0) { return cur_len; } len = snprintf(&buffer[cur_len], buffer_len - cur_len, " %" PRIu64 " %" PRIu64, entry.start_ns, entry.end_ns); if (len < 0) { return 0; } return cur_len + len; } std::string CreateStringFromEntry(const Entry& entry) { std::string line(kBufferLen, '\0'); size_t size = FormatEntry(entry, line.data(), line.size()); if (size == 0) { return ""; } line.resize(size); return line; } bool WriteEntryToFd(int fd, const Entry& entry) { char buffer[kBufferLen]; size_t size = FormatEntry(entry, buffer, sizeof(buffer)); if (size == 0 || size == sizeof(buffer)) { return false; } buffer[size++] = '\n'; buffer[size] = '\0'; ssize_t bytes = TEMP_FAILURE_RETRY(write(fd, buffer, size)); if (bytes < 0 || static_cast(bytes) != size) { return false; } return true; } } // namespace memory_trace