blob: e6d21dfc42ddcff0ee5ec3dcede43cc1eb254bc0 [file] [log] [blame]
/*
* 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 <inttypes.h>
#include <stdio.h>
#include <unistd.h>
#include <string>
#include <android-base/stringprintf.h>
#include <memory_trace/MemoryTrace.h>
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<size_t>(bytes) != size) {
return false;
}
return true;
}
} // namespace memory_trace