diff options
Diffstat (limited to 'memory_replay/main.cpp')
-rw-r--r-- | memory_replay/main.cpp | 151 |
1 files changed, 83 insertions, 68 deletions
diff --git a/memory_replay/main.cpp b/memory_replay/main.cpp index 9873ec70..42b52983 100644 --- a/memory_replay/main.cpp +++ b/memory_replay/main.cpp @@ -18,7 +18,6 @@ #include <errno.h> #include <fcntl.h> #include <inttypes.h> -#include <malloc.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> @@ -27,67 +26,95 @@ #include <sys/types.h> #include <unistd.h> -#include "Alloc.h" -#include "File.h" +#include "Action.h" +#include "LineBuffer.h" #include "NativeInfo.h" #include "Pointers.h" #include "Thread.h" #include "Threads.h" -constexpr size_t kDefaultMaxThreads = 512; +static char g_buffer[65535]; -static size_t GetMaxAllocs(const AllocEntry* entries, size_t num_entries) { +size_t GetMaxAllocs(int fd) { + lseek(fd, 0, SEEK_SET); + LineBuffer line_buf(fd, g_buffer, sizeof(g_buffer)); + char* line; + size_t line_len; size_t num_allocs = 0; - for (size_t i = 0; i < num_entries; i++) { - switch (entries[i].type) { - case THREAD_DONE: - break; - case MALLOC: - case CALLOC: - case MEMALIGN: - case REALLOC: - num_allocs++; - break; - case FREE: + while (line_buf.GetLine(&line, &line_len)) { + char* word = reinterpret_cast<char*>(memchr(line, ':', line_len)); + if (word == nullptr) { + continue; + } + + word++; + while (*word++ == ' '); + // This will treat a realloc as an allocation, even if it frees + // another allocation. Since reallocs are relatively rare, this + // shouldn't inflate the numbers that much. + if (*word == 'f') { + // Check if this is a free of zero. + uintptr_t pointer; + if (sscanf(word, "free %" SCNxPTR, &pointer) == 1 && pointer != 0) { num_allocs--; - break; + } + } else if (*word != 't') { + // Skip the thread_done message. + num_allocs++; } } return num_allocs; } -static void ProcessDump(const AllocEntry* entries, size_t num_entries, size_t max_threads) { - // Do a pass to get the maximum number of allocations used at one - // time to allow a single mmap that can hold the maximum number of - // pointers needed at once. - size_t max_allocs = GetMaxAllocs(entries, num_entries); +void ProcessDump(int fd, size_t max_allocs, size_t max_threads) { + lseek(fd, 0, SEEK_SET); Pointers pointers(max_allocs); Threads threads(&pointers, max_threads); - NativePrintf("Maximum threads available: %zu\n", threads.max_threads()); - NativePrintf("Maximum allocations in dump: %zu\n", max_allocs); - NativePrintf("Total pointers available: %zu\n\n", pointers.max_pointers()); - - NativePrintInfo("Initial "); - - for (size_t i = 0; i < num_entries; i++) { - if (((i + 1) % 100000) == 0) { - NativePrintf(" At line %zu:\n", i + 1); - NativePrintInfo(" "); + printf("Maximum threads available: %zu\n", threads.max_threads()); + printf("Maximum allocations in dump: %zu\n", max_allocs); + printf("Total pointers available: %zu\n", pointers.max_pointers()); + printf("\n"); + + PrintNativeInfo("Initial "); + + LineBuffer line_buf(fd, g_buffer, sizeof(g_buffer)); + char* line; + size_t line_len; + size_t line_number = 0; + while (line_buf.GetLine(&line, &line_len)) { + pid_t tid; + int line_pos = 0; + char type[128]; + uintptr_t key_pointer; + + // Every line is of this format: + // <tid>: <action_type> <pointer> + // Some actions have extra arguments which will be used and verified + // when creating the Action object. + if (sscanf(line, "%d: %s %" SCNxPTR " %n", &tid, type, &key_pointer, &line_pos) != 3) { + err(1, "Unparseable line found: %s\n", line); } - const AllocEntry& entry = entries[i]; - Thread* thread = threads.FindThread(entry.tid); + line_number++; + if ((line_number % 100000) == 0) { + printf(" At line %zu:\n", line_number); + PrintNativeInfo(" "); + } + Thread* thread = threads.FindThread(tid); if (thread == nullptr) { - thread = threads.CreateThread(entry.tid); + thread = threads.CreateThread(tid); } // Wait for the thread to complete any previous actions before handling // the next action. thread->WaitForReady(); - thread->SetAllocEntry(&entry); + Action* action = thread->CreateAction(key_pointer, type, line + line_pos); + if (action == nullptr) { + err(1, "Cannot create action from line: %s\n", line); + } - bool does_free = AllocDoesFree(entry); + bool does_free = action->DoesFree(); if (does_free) { // Make sure that any other threads doing allocations are complete // before triggering the action. Otherwise, another thread could @@ -98,7 +125,7 @@ static void ProcessDump(const AllocEntry* entries, size_t num_entries, size_t ma // Tell the thread to execute the action. thread->SetPending(); - if (entries[i].type == THREAD_DONE) { + if (action->EndThread()) { // Wait for the thread to finish and clear the thread entry. threads.Finish(thread); } @@ -113,7 +140,7 @@ static void ProcessDump(const AllocEntry* entries, size_t num_entries, size_t ma // Wait for all threads to stop processing actions. threads.WaitForAllToQuiesce(); - NativePrintInfo("Final "); + PrintNativeInfo("Final "); // Free any outstanding pointers. // This allows us to run a tool like valgrind to verify that no memory @@ -122,12 +149,12 @@ static void ProcessDump(const AllocEntry* entries, size_t num_entries, size_t ma pointers.FreeAll(); // Print out the total time making all allocation calls. - char buffer[256]; - uint64_t total_nsecs = threads.total_time_nsecs(); - NativeFormatFloat(buffer, sizeof(buffer), total_nsecs, 1000000000); - NativePrintf("Total Allocation/Free Time: %" PRIu64 "ns %ss\n", total_nsecs, buffer); + printf("Total Allocation/Free Time: %" PRIu64 "ns %0.2fs\n", + threads.total_time_nsecs(), threads.total_time_nsecs()/1000000000.0); } +constexpr size_t DEFAULT_MAX_THREADS = 512; + int main(int argc, char** argv) { if (argc != 2 && argc != 3) { if (argc > 3) { @@ -136,41 +163,29 @@ int main(int argc, char** argv) { fprintf(stderr, "Requires at least one argument.\n"); } fprintf(stderr, "Usage: %s MEMORY_LOG_FILE [MAX_THREADS]\n", basename(argv[0])); - fprintf(stderr, " MEMORY_LOG_FILE\n"); - fprintf(stderr, " This can either be a text file or a zipped text file.\n"); - fprintf(stderr, " MAX_THREADs\n"); - fprintf(stderr, " The maximum number of threads in the trace. The default is %zu.\n", - kDefaultMaxThreads); - fprintf(stderr, " This pre-allocates the memory for thread data to avoid allocating\n"); - fprintf(stderr, " while the trace is being replayed.\n"); return 1; } -#if defined(__LP64__) - NativePrintf("64 bit environment.\n"); -#else - NativePrintf("32 bit environment.\n"); -#endif - -#if defined(__BIONIC__) - NativePrintf("Setting decay time to 1\n"); - mallopt(M_DECAY_TIME, 1); -#endif - - size_t max_threads = kDefaultMaxThreads; + size_t max_threads = DEFAULT_MAX_THREADS; if (argc == 3) { max_threads = atoi(argv[2]); } - AllocEntry* entries; - size_t num_entries; - GetUnwindInfo(argv[1], &entries, &num_entries); + int dump_fd = open(argv[1], O_RDONLY); + if (dump_fd == -1) { + fprintf(stderr, "Failed to open %s: %s\n", argv[1], strerror(errno)); + return 1; + } - NativePrintf("Processing: %s\n", argv[1]); + printf("Processing: %s\n", argv[1]); - ProcessDump(entries, num_entries, max_threads); + // Do a first pass to get the total number of allocations used at one + // time to allow a single mmap that can hold the maximum number of + // pointers needed at once. + size_t max_allocs = GetMaxAllocs(dump_fd); + ProcessDump(dump_fd, max_allocs, max_threads); - FreeEntries(entries, num_entries); + close(dump_fd); return 0; } |