summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Ferris <cferris@google.com>2019-08-30 11:17:55 -0700
committerandroid-build-merger <android-build-merger@google.com>2019-08-30 11:17:55 -0700
commit2077beb1a9f11742e5273bd68807d084ab75474c (patch)
tree16d4d05908a3ac824f2d0f911d966724ee964e68
parent64146b2a17afb2550cf4d305dd06d87d087cec13 (diff)
parent7ac993ce67420cc2e2b06c8b8f97e99f1a8e7d03 (diff)
downloadextras-2077beb1a9f11742e5273bd68807d084ab75474c.tar.gz
Merge "Refactor code with single trace parser."
am: 7ac993ce67 Change-Id: Iff4618349bce7e22456bf004b861daafcc0328f6
-rw-r--r--memory_replay/Action.cpp211
-rw-r--r--memory_replay/Action.h45
-rw-r--r--memory_replay/Alloc.cpp178
-rw-r--r--memory_replay/Alloc.h49
-rw-r--r--memory_replay/Android.bp31
-rw-r--r--memory_replay/LineBuffer.cpp59
-rw-r--r--memory_replay/NativeInfo.cpp93
-rw-r--r--memory_replay/NativeInfo.h15
-rw-r--r--memory_replay/Pointers.cpp10
-rw-r--r--memory_replay/Pointers.h15
-rw-r--r--memory_replay/Thread.cpp5
-rw-r--r--memory_replay/Thread.h19
-rw-r--r--memory_replay/Threads.cpp19
-rw-r--r--memory_replay/Threads.h6
-rw-r--r--memory_replay/TraceBenchmark.cpp227
-rw-r--r--memory_replay/Utils.h33
-rw-r--r--memory_replay/Zip.cpp157
-rw-r--r--memory_replay/Zip.h (renamed from memory_replay/LineBuffer.h)24
-rw-r--r--memory_replay/main.cpp139
-rw-r--r--memory_replay/tests/ActionTest.cpp168
-rw-r--r--memory_replay/tests/AllocTest.cpp146
-rw-r--r--memory_replay/tests/LineBufferTest.cpp241
-rw-r--r--memory_replay/tests/NativeInfoTest.cpp10
-rw-r--r--memory_replay/tests/ThreadTest.cpp17
-rw-r--r--memory_replay/tests/ThreadsTest.cpp29
-rw-r--r--memory_replay/tests/ZipTest.cpp73
-rw-r--r--memory_replay/tests/test.zipbin0 -> 201 bytes
-rw-r--r--memory_replay/traces/README2
28 files changed, 930 insertions, 1091 deletions
diff --git a/memory_replay/Action.cpp b/memory_replay/Action.cpp
deleted file mode 100644
index 7f15012f..00000000
--- a/memory_replay/Action.cpp
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2015 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 <malloc.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/param.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <new>
-
-#include "Action.h"
-#include "Pointers.h"
-#include "Threads.h"
-
-static __always_inline uint64_t Nanotime() {
- struct timespec t;
- t.tv_sec = t.tv_nsec = 0;
- clock_gettime(CLOCK_MONOTONIC, &t);
- return static_cast<uint64_t>(t.tv_sec) * 1000000000LL + t.tv_nsec;
-}
-
-static __always_inline void MakeAllocationResident(void* ptr, size_t nbytes, int pagesize) {
- uint8_t* data = reinterpret_cast<uint8_t*>(ptr);
- for (size_t i = 0; i < nbytes; i += pagesize) {
- data[i] = 1;
- }
-}
-
-class EndThreadAction : public Action {
- public:
- EndThreadAction() {}
-
- bool EndThread() override { return true; }
-
- uint64_t Execute(Pointers*) override { return 0; }
-};
-
-class AllocAction : public Action {
- public:
- explicit AllocAction(uintptr_t key_pointer) : key_pointer_(key_pointer) {}
-
- protected:
- uintptr_t key_pointer_ = 0;
- size_t size_ = 0;
-};
-
-class MallocAction : public AllocAction {
- public:
- MallocAction(uintptr_t key_pointer, const char* line) : AllocAction(key_pointer) {
- if (sscanf(line, "%zu", &size_) != 1) {
- is_error_ = true;
- }
- }
-
- uint64_t Execute(Pointers* pointers) override {
- int pagesize = getpagesize();
- uint64_t time_nsecs = Nanotime();
- void* memory = malloc(size_);
- MakeAllocationResident(memory, size_, pagesize);
- time_nsecs = Nanotime() - time_nsecs;
-
- pointers->Add(key_pointer_, memory);
-
- return time_nsecs;
- }
-};
-
-class CallocAction : public AllocAction {
- public:
- CallocAction(uintptr_t key_pointer, const char* line) : AllocAction(key_pointer) {
- if (sscanf(line, "%zu %zu", &n_elements_, &size_) != 2) {
- is_error_ = true;
- }
- }
-
- uint64_t Execute(Pointers* pointers) override {
- int pagesize = getpagesize();
- uint64_t time_nsecs = Nanotime();
- void* memory = calloc(n_elements_, size_);
- MakeAllocationResident(memory, n_elements_ * size_, pagesize);
- time_nsecs = Nanotime() - time_nsecs;
-
- pointers->Add(key_pointer_, memory);
-
- return time_nsecs;
- }
-
- private:
- size_t n_elements_ = 0;
-};
-
-class ReallocAction : public AllocAction {
- public:
- ReallocAction(uintptr_t key_pointer, const char* line) : AllocAction(key_pointer) {
- if (sscanf(line, "%" SCNxPTR " %zu", &old_pointer_, &size_) != 2) {
- is_error_ = true;
- }
- }
-
- bool DoesFree() override { return old_pointer_ != 0; }
-
- uint64_t Execute(Pointers* pointers) override {
- void* old_memory = nullptr;
- if (old_pointer_ != 0) {
- old_memory = pointers->Remove(old_pointer_);
- }
-
- int pagesize = getpagesize();
- uint64_t time_nsecs = Nanotime();
- void* memory = realloc(old_memory, size_);
- MakeAllocationResident(memory, size_, pagesize);
- time_nsecs = Nanotime() - time_nsecs;
-
- pointers->Add(key_pointer_, memory);
-
- return time_nsecs;
- }
-
- private:
- uintptr_t old_pointer_ = 0;
-};
-
-class MemalignAction : public AllocAction {
- public:
- MemalignAction(uintptr_t key_pointer, const char* line) : AllocAction(key_pointer) {
- if (sscanf(line, "%zu %zu", &align_, &size_) != 2) {
- is_error_ = true;
- }
- }
-
- uint64_t Execute(Pointers* pointers) override {
- int pagesize = getpagesize();
- uint64_t time_nsecs = Nanotime();
- void* memory = memalign(align_, size_);
- MakeAllocationResident(memory, size_, pagesize);
- time_nsecs = Nanotime() - time_nsecs;
-
- pointers->Add(key_pointer_, memory);
-
- return time_nsecs;
- }
-
- private:
- size_t align_ = 0;
-};
-
-class FreeAction : public AllocAction {
- public:
- explicit FreeAction(uintptr_t key_pointer) : AllocAction(key_pointer) {}
-
- bool DoesFree() override { return key_pointer_ != 0; }
-
- uint64_t Execute(Pointers* pointers) override {
- if (key_pointer_) {
- void* memory = pointers->Remove(key_pointer_);
- uint64_t time_nsecs = Nanotime();
- free(memory);
- return Nanotime() - time_nsecs;
- }
- return 0;
- }
-};
-
-size_t Action::MaxActionSize() {
- size_t max = MAX(sizeof(EndThreadAction), sizeof(MallocAction));
- max = MAX(max, sizeof(CallocAction));
- max = MAX(max, sizeof(ReallocAction));
- max = MAX(max, sizeof(MemalignAction));
- return MAX(max, sizeof(FreeAction));
-}
-
-Action* Action::CreateAction(uintptr_t key_pointer, const char* type, const char* line,
- void* action_memory) {
- Action* action = nullptr;
- if (strcmp(type, "malloc") == 0) {
- action = new (action_memory) MallocAction(key_pointer, line);
- } else if (strcmp(type, "free") == 0) {
- action = new (action_memory) FreeAction(key_pointer);
- } else if (strcmp(type, "calloc") == 0) {
- action = new (action_memory) CallocAction(key_pointer, line);
- } else if (strcmp(type, "realloc") == 0) {
- action = new (action_memory) ReallocAction(key_pointer, line);
- } else if (strcmp(type, "memalign") == 0) {
- action = new (action_memory) MemalignAction(key_pointer, line);
- } else if (strcmp(type, "thread_done") == 0) {
- action = new (action_memory) EndThreadAction();
- }
-
- if (action == nullptr || action->IsError()) {
- return nullptr;
- }
- return action;
-}
diff --git a/memory_replay/Action.h b/memory_replay/Action.h
deleted file mode 100644
index f498f526..00000000
--- a/memory_replay/Action.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#ifndef _MEMORY_REPLAY_ACTION_H
-#define _MEMORY_REPLAY_ACTION_H
-
-#include <stdint.h>
-
-class Pointers;
-
-class Action {
- public:
- Action() {}
- virtual ~Action() {}
-
- virtual uint64_t Execute(Pointers* pointers) = 0;
-
- bool IsError() { return is_error_; };
-
- virtual bool EndThread() { return false; }
-
- virtual bool DoesFree() { return false; }
-
- static size_t MaxActionSize();
- static Action* CreateAction(uintptr_t key_pointer, const char* type,
- const char* line, void* action_memory);
-
- protected:
- bool is_error_ = false;
-};
-
-#endif // _MEMORY_REPLAY_ACTION_H
diff --git a/memory_replay/Alloc.cpp b/memory_replay/Alloc.cpp
new file mode 100644
index 00000000..af94ee5d
--- /dev/null
+++ b/memory_replay/Alloc.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2019 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 <err.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "Alloc.h"
+#include "Pointers.h"
+#include "Utils.h"
+
+void AllocGetData(const std::string& line, AllocEntry* entry) {
+ int line_pos = 0;
+ char name[128];
+ // All lines have this format:
+ // TID: ALLOCATION_TYPE POINTER
+ // 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
+ if (sscanf(line.c_str(), "%d: %127s %" SCNx64 " %n", &entry->tid, name, &entry->ptr, &line_pos) !=
+ 3) {
+ errx(1, "File Error: Failed to process %s", line.c_str());
+ }
+ const char* line_end = &line[line_pos];
+ std::string type(name);
+ if (type == "malloc") {
+ // Format:
+ // TID: malloc POINTER SIZE_OF_ALLOCATION
+ if (sscanf(line_end, "%zu", &entry->size) != 1) {
+ errx(1, "File Error: Failed to read malloc data %s", line.c_str());
+ }
+ entry->type = MALLOC;
+ } else if (type == "free") {
+ // Format:
+ // TID: free POINTER
+ entry->type = FREE;
+ } else if (type == "calloc") {
+ // Format:
+ // TID: calloc POINTER ITEM_COUNT ITEM_SIZE
+ if (sscanf(line_end, "%" SCNd64 " %zu", &entry->u.n_elements, &entry->size) != 2) {
+ errx(1, "File Error: Failed to read calloc data %s", line.c_str());
+ }
+ entry->type = CALLOC;
+ } else if (type == "realloc") {
+ // Format:
+ // TID: calloc POINTER NEW_SIZE OLD_POINTER
+ if (sscanf(line_end, "%" SCNx64 " %zu", &entry->u.old_ptr, &entry->size) != 2) {
+ errx(1, "File Error: Failed to read realloc data %s", line.c_str());
+ }
+ entry->type = REALLOC;
+ } else if (type == "memalign") {
+ // Format:
+ // TID: memalign POINTER ALIGNMENT SIZE
+ if (sscanf(line_end, "%" SCNd64 " %zu", &entry->u.align, &entry->size) != 2) {
+ errx(1, "File Error: Failed to read memalign data %s", line.c_str());
+ }
+ entry->type = MEMALIGN;
+ } else if (type == "thread_done") {
+ entry->type = THREAD_DONE;
+ } else {
+ errx(1, "File Error: Unknown type %s", type.c_str());
+ }
+}
+
+bool AllocDoesFree(const AllocEntry& entry) {
+ switch (entry.type) {
+ case MALLOC:
+ case CALLOC:
+ case MEMALIGN:
+ case THREAD_DONE:
+ return false;
+
+ case FREE:
+ return entry.ptr != 0;
+
+ case REALLOC:
+ return entry.u.old_ptr != 0;
+ }
+}
+
+static uint64_t MallocExecute(const AllocEntry& entry, Pointers* pointers) {
+ int pagesize = getpagesize();
+ uint64_t time_nsecs = Nanotime();
+ void* memory = malloc(entry.size);
+ MakeAllocationResident(memory, entry.size, pagesize);
+ time_nsecs = Nanotime() - time_nsecs;
+
+ pointers->Add(entry.ptr, memory);
+
+ return time_nsecs;
+}
+
+static uint64_t CallocExecute(const AllocEntry& entry, Pointers* pointers) {
+ int pagesize = getpagesize();
+ uint64_t time_nsecs = Nanotime();
+ void* memory = calloc(entry.u.n_elements, entry.size);
+ MakeAllocationResident(memory, entry.u.n_elements * entry.size, pagesize);
+ time_nsecs = Nanotime() - time_nsecs;
+
+ pointers->Add(entry.ptr, memory);
+
+ return time_nsecs;
+}
+
+static uint64_t ReallocExecute(const AllocEntry& entry, Pointers* pointers) {
+ void* old_memory = nullptr;
+ if (entry.u.old_ptr != 0) {
+ old_memory = pointers->Remove(entry.u.old_ptr);
+ }
+
+ int pagesize = getpagesize();
+ uint64_t time_nsecs = Nanotime();
+ void* memory = realloc(old_memory, entry.size);
+ MakeAllocationResident(memory, entry.size, pagesize);
+ time_nsecs = Nanotime() - time_nsecs;
+
+ pointers->Add(entry.ptr, memory);
+
+ return time_nsecs;
+}
+
+static uint64_t MemalignExecute(const AllocEntry& entry, Pointers* pointers) {
+ int pagesize = getpagesize();
+ uint64_t time_nsecs = Nanotime();
+ void* memory = memalign(entry.u.align, entry.size);
+ MakeAllocationResident(memory, entry.size, pagesize);
+ time_nsecs = Nanotime() - time_nsecs;
+
+ pointers->Add(entry.ptr, memory);
+
+ return time_nsecs;
+}
+
+static uint64_t FreeExecute(const AllocEntry& entry, Pointers* pointers) {
+ if (entry.ptr == 0) {
+ return 0;
+ }
+
+ void* memory = pointers->Remove(entry.ptr);
+ uint64_t time_nsecs = Nanotime();
+ free(memory);
+ return Nanotime() - time_nsecs;
+}
+
+uint64_t AllocExecute(const AllocEntry& entry, Pointers* pointers) {
+ switch (entry.type) {
+ case MALLOC:
+ return MallocExecute(entry, pointers);
+ case CALLOC:
+ return CallocExecute(entry, pointers);
+ case REALLOC:
+ return ReallocExecute(entry, pointers);
+ case MEMALIGN:
+ return MemalignExecute(entry, pointers);
+ case FREE:
+ return FreeExecute(entry, pointers);
+ default:
+ return 0;
+ }
+}
diff --git a/memory_replay/Alloc.h b/memory_replay/Alloc.h
new file mode 100644
index 00000000..d590fcba
--- /dev/null
+++ b/memory_replay/Alloc.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <string>
+
+// Forward Declarations.
+class Pointers;
+
+enum AllocEnum : uint8_t {
+ MALLOC = 0,
+ CALLOC,
+ MEMALIGN,
+ REALLOC,
+ FREE,
+ THREAD_DONE,
+};
+
+struct AllocEntry {
+ pid_t tid;
+ AllocEnum type;
+ uint64_t ptr = 0;
+ size_t size = 0;
+ union {
+ uint64_t old_ptr = 0;
+ uint64_t n_elements;
+ uint64_t align;
+ } u;
+};
+
+void AllocGetData(const std::string& line, AllocEntry* entry);
+
+bool AllocDoesFree(const AllocEntry& entry);
+
+uint64_t AllocExecute(const AllocEntry& entry, Pointers* pointers);
diff --git a/memory_replay/Android.bp b/memory_replay/Android.bp
index d8217d69..c5cd3cd5 100644
--- a/memory_replay/Android.bp
+++ b/memory_replay/Android.bp
@@ -16,7 +16,7 @@
cc_defaults {
name: "memory_flag_defaults",
- host_supported: true,
+ host_supported: false,
cflags: [
"-Wall",
@@ -24,12 +24,6 @@ cc_defaults {
"-Werror",
],
- target: {
- darwin: {
- enabled: false,
- },
- },
-
compile_multilib: "both",
}
@@ -38,15 +32,22 @@ cc_defaults {
defaults: ["memory_flag_defaults"],
srcs: [
- "Action.cpp",
- "LineBuffer.cpp",
+ "Alloc.cpp",
"NativeInfo.cpp",
"Pointers.cpp",
"Thread.cpp",
"Threads.cpp",
+ "Zip.cpp",
],
- shared_libs: ["libbase"],
+ shared_libs: [
+ "libbase",
+ "libziparchive",
+ ],
+
+ static_libs: [
+ "libasync_safe",
+ ],
}
cc_binary {
@@ -71,12 +72,12 @@ cc_test {
isolated: true,
srcs: [
- "tests/ActionTest.cpp",
- "tests/LineBufferTest.cpp",
+ "tests/AllocTest.cpp",
"tests/NativeInfoTest.cpp",
"tests/PointersTest.cpp",
"tests/ThreadTest.cpp",
"tests/ThreadsTest.cpp",
+ "tests/ZipTest.cpp",
],
local_include_dirs: ["tests"],
@@ -86,6 +87,10 @@ cc_test {
test_suites: ["device-tests"],
},
},
+
+ data: [
+ "tests/test.zip",
+ ],
}
cc_benchmark {
@@ -93,7 +98,9 @@ cc_benchmark {
defaults: ["memory_flag_defaults"],
srcs: [
+ "Alloc.cpp",
"TraceBenchmark.cpp",
+ "Zip.cpp",
],
shared_libs: [
diff --git a/memory_replay/LineBuffer.cpp b/memory_replay/LineBuffer.cpp
deleted file mode 100644
index 5e65ad62..00000000
--- a/memory_replay/LineBuffer.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2015 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 <errno.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "LineBuffer.h"
-
-LineBuffer::LineBuffer(int fd, char* buffer, size_t buffer_len) : fd_(fd), buffer_(buffer), buffer_len_(buffer_len) {
-}
-
-bool LineBuffer::GetLine(char** line, size_t* line_len) {
- while (true) {
- if (bytes_ > 0) {
- char* newline = reinterpret_cast<char*>(memchr(buffer_ + start_, '\n', bytes_));
- if (newline != nullptr) {
- *newline = '\0';
- *line = buffer_ + start_;
- start_ = newline - buffer_ + 1;
- bytes_ -= newline - *line + 1;
- *line_len = newline - *line;
- return true;
- }
- }
- if (start_ > 0) {
- // Didn't find anything, copy the current to the front of the buffer.
- memmove(buffer_, buffer_ + start_, bytes_);
- start_ = 0;
- }
- ssize_t bytes = TEMP_FAILURE_RETRY(read(fd_, buffer_ + bytes_, buffer_len_ - bytes_ - 1));
- if (bytes <= 0) {
- if (bytes_ > 0) {
- // The read data might not contain a nul terminator, so add one.
- buffer_[bytes_] = '\0';
- *line = buffer_ + start_;
- *line_len = bytes_;
- bytes_ = 0;
- start_ = 0;
- return true;
- }
- return false;
- }
- bytes_ += bytes;
- }
-}
diff --git a/memory_replay/NativeInfo.cpp b/memory_replay/NativeInfo.cpp
index 2db653cb..8d0e4945 100644
--- a/memory_replay/NativeInfo.cpp
+++ b/memory_replay/NativeInfo.cpp
@@ -18,50 +18,87 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
+#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
-#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/types.h>
#include <unistd.h>
#include <android-base/unique_fd.h>
+#include <async_safe/log.h>
-#include "LineBuffer.h"
#include "NativeInfo.h"
+void NativePrintf(const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ char buffer[512];
+ int buffer_len = async_safe_format_buffer_va_list(buffer, sizeof(buffer), fmt, args);
+ va_end(args);
+
+ (void)write(STDOUT_FILENO, buffer, buffer_len);
+}
+
+void NativeFormatFloat(char* buffer, size_t buffer_len, uint64_t value, uint64_t divisor) {
+ uint64_t hundreds = ((((value % divisor) * 1000) / divisor) + 5) / 10;
+ async_safe_format_buffer(buffer, buffer_len, "%" PRIu64 ".%02" PRIu64, value / divisor, hundreds);
+}
+
// This function is not re-entrant since it uses a static buffer for
// the line data.
-void GetNativeInfo(int smaps_fd, size_t* rss_bytes, size_t* va_bytes) {
- static char map_buffer[65535];
- LineBuffer line_buf(smaps_fd, map_buffer, sizeof(map_buffer));
- char* line;
+void NativeGetInfo(int smaps_fd, size_t* rss_bytes, size_t* va_bytes) {
size_t total_rss_bytes = 0;
size_t total_va_bytes = 0;
- size_t line_len;
bool native_map = false;
- while (line_buf.GetLine(&line, &line_len)) {
- uintptr_t start, end;
- int name_pos;
- size_t native_rss_kB;
- if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %*4s %*x %*x:%*x %*d %n",
- &start, &end, &name_pos) == 2) {
- if (strcmp(line + name_pos, "[anon:libc_malloc]") == 0 ||
- strcmp(line + name_pos, "[heap]") == 0) {
- total_va_bytes += end - start;
- native_map = true;
- } else {
- native_map = false;
+
+ char buf[1024];
+ size_t buf_start = 0;
+ size_t buf_bytes = 0;
+ while (true) {
+ ssize_t bytes =
+ TEMP_FAILURE_RETRY(read(smaps_fd, buf + buf_bytes, sizeof(buf) - buf_bytes - 1));
+ if (bytes <= 0) {
+ break;
+ }
+ buf_bytes += bytes;
+ while (buf_bytes > 0) {
+ char* newline = reinterpret_cast<char*>(memchr(&buf[buf_start], '\n', buf_bytes));
+ if (newline == nullptr) {
+ break;
+ }
+ *newline = '\0';
+ uintptr_t start, end;
+ int name_pos;
+ size_t native_rss_kB;
+ if (sscanf(&buf[buf_start], "%" SCNxPTR "-%" SCNxPTR " %*4s %*x %*x:%*x %*d %n", &start, &end,
+ &name_pos) == 2) {
+ char* map_name = &buf[buf_start + name_pos];
+ if (strcmp(map_name, "[anon:libc_malloc]") == 0 || strcmp(map_name, "[heap]") == 0) {
+ total_va_bytes += end - start;
+ native_map = true;
+ } else {
+ native_map = false;
+ }
+ } else if (native_map && sscanf(&buf[buf_start], "Rss: %zu", &native_rss_kB) == 1) {
+ total_rss_bytes += native_rss_kB * 1024;
}
- } else if (native_map && sscanf(line, "Rss: %zu", &native_rss_kB) == 1) {
- total_rss_bytes += native_rss_kB * 1024;
+ buf_bytes -= newline - &buf[buf_start] + 1;
+ buf_start = newline - buf + 1;
+ }
+ if (buf_start > 0) {
+ if (buf_bytes > 0) {
+ memmove(buf, &buf[buf_start], buf_bytes);
+ }
+ buf_start = 0;
}
}
*rss_bytes = total_rss_bytes;
*va_bytes = total_va_bytes;
}
-void PrintNativeInfo(const char* preamble) {
+void NativePrintInfo(const char* preamble) {
size_t rss_bytes;
size_t va_bytes;
@@ -70,8 +107,12 @@ void PrintNativeInfo(const char* preamble) {
err(1, "Cannot open /proc/self/smaps: %s\n", strerror(errno));
}
- GetNativeInfo(smaps_fd, &rss_bytes, &va_bytes);
- printf("%sNative RSS: %zu bytes %0.2fMB\n", preamble, rss_bytes, rss_bytes/(1024*1024.0));
- printf("%sNative VA Space: %zu bytes %0.2fMB\n", preamble, va_bytes, va_bytes/(1024*1024.0));
- fflush(stdout);
+ NativeGetInfo(smaps_fd, &rss_bytes, &va_bytes);
+
+ // Avoid any allocations, so use special non-allocating printfs.
+ char buffer[256];
+ NativeFormatFloat(buffer, sizeof(buffer), rss_bytes, 1024 * 1024);
+ NativePrintf("%sNative RSS: %zu bytes %sMB\n", preamble, rss_bytes, buffer);
+ NativeFormatFloat(buffer, sizeof(buffer), va_bytes, 1024 * 1024);
+ NativePrintf("%sNative VA Space: %zu bytes %sMB\n", preamble, va_bytes, buffer);
}
diff --git a/memory_replay/NativeInfo.h b/memory_replay/NativeInfo.h
index 40a16f2d..c91eec29 100644
--- a/memory_replay/NativeInfo.h
+++ b/memory_replay/NativeInfo.h
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-#ifndef _MEMORY_REPLAY_NATIVE_INFO_H
-#define _MEMORY_REPLAY_NATIVE_INFO_H
+#pragma once
-// This function is not re-entrant.
-void GetNativeInfo(int smaps_fd, size_t* rss_bytes, size_t* va_bytes);
+void NativeGetInfo(int smaps_fd, size_t* rss_bytes, size_t* va_bytes);
-// This function is not re-entrant.
-void PrintNativeInfo(const char* preamble);
+void NativePrintInfo(const char* preamble);
-#endif // _MEMORY_REPLAY_NATIVE_INFO_H
+// Does not support any floating point specifiers.
+void NativePrintf(const char* fmt, ...) __printflike(1, 2);
+
+// Fill buffer as if %0.2f was chosen for value / divisor.
+void NativeFormatFloat(char* buffer, size_t buffer_len, uint64_t value, uint64_t divisor);
diff --git a/memory_replay/Pointers.cpp b/memory_replay/Pointers.cpp
index e9eebadb..6335dc2c 100644
--- a/memory_replay/Pointers.cpp
+++ b/memory_replay/Pointers.cpp
@@ -32,12 +32,12 @@ Pointers::Pointers(size_t max_allocs) {
// Align to a page.
pointers_size_ = (max_allocs * 4 * sizeof(pointer_data) + pagesize - 1) & ~(pagesize - 1);
max_pointers_ = pointers_size_ / sizeof(pointer_data);
- void* memory = mmap(nullptr, pointers_size_, PROT_READ | PROT_WRITE,
- MAP_ANON | MAP_PRIVATE, -1, 0);
+ void* memory =
+ mmap(nullptr, pointers_size_, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
if (memory == MAP_FAILED) {
err(1, "Unable to allocate data for pointer hash: %zu total_allocs\n", max_allocs);
}
- // Make sure that all of the RSS for this is counted right away.
+ // Set all of the pointers to be empty.
memset(memory, 0, pointers_size_);
pointers_ = reinterpret_cast<pointer_data*>(memory);
}
@@ -74,7 +74,7 @@ void* Pointers::Remove(uintptr_t key_pointer) {
return pointer;
}
-pointer_data* Pointers::Find(uintptr_t key_pointer) {
+Pointers::pointer_data* Pointers::Find(uintptr_t key_pointer) {
size_t index = GetHash(key_pointer);
for (size_t entries = max_pointers_; entries != 0; entries--) {
if (atomic_load(&pointers_[index].key_pointer) == key_pointer) {
@@ -87,7 +87,7 @@ pointer_data* Pointers::Find(uintptr_t key_pointer) {
return nullptr;
}
-pointer_data* Pointers::FindEmpty(uintptr_t key_pointer) {
+Pointers::pointer_data* Pointers::FindEmpty(uintptr_t key_pointer) {
size_t index = GetHash(key_pointer);
for (size_t entries = 0; entries < max_pointers_; entries++) {
uintptr_t empty = 0;
diff --git a/memory_replay/Pointers.h b/memory_replay/Pointers.h
index c7cd825a..040027bf 100644
--- a/memory_replay/Pointers.h
+++ b/memory_replay/Pointers.h
@@ -14,19 +14,18 @@
* limitations under the License.
*/
-#ifndef _MEMORY_REPLAY_POINTERS_H
-#define _MEMORY_REPLAY_POINTERS_H
+#pragma once
#include <stdatomic.h>
#include <stdint.h>
-struct pointer_data {
- std::atomic_uintptr_t key_pointer;
- void* pointer;
-};
-
class Pointers {
public:
+ struct pointer_data {
+ std::atomic_uintptr_t key_pointer;
+ void* pointer;
+ };
+
explicit Pointers(size_t max_allocs);
virtual ~Pointers();
@@ -47,5 +46,3 @@ class Pointers {
size_t pointers_size_ = 0;
size_t max_pointers_ = 0;
};
-
-#endif // _MEMORY_REPLAY_POINTERS_H
diff --git a/memory_replay/Thread.cpp b/memory_replay/Thread.cpp
index 497b288e..4f0a6d54 100644
--- a/memory_replay/Thread.cpp
+++ b/memory_replay/Thread.cpp
@@ -16,7 +16,6 @@
#include <pthread.h>
-#include "Action.h"
#include "Thread.h"
Thread::Thread() {
@@ -56,7 +55,3 @@ void Thread::ClearPending() {
pthread_mutex_unlock(&mutex_);
pthread_cond_signal(&cond_);
}
-
-Action* Thread::CreateAction(uintptr_t key_pointer, const char* type, const char* line) {
- return Action::CreateAction(key_pointer, type, line, action_memory_);
-}
diff --git a/memory_replay/Thread.h b/memory_replay/Thread.h
index 7724c12e..ffab01b4 100644
--- a/memory_replay/Thread.h
+++ b/memory_replay/Thread.h
@@ -14,18 +14,16 @@
* limitations under the License.
*/
-#ifndef _MEMORY_REPLAY_THREAD_H
-#define _MEMORY_REPLAY_THREAD_H
+#pragma once
#include <pthread.h>
#include <stdint.h>
#include <sys/types.h>
-class Action;
+// Forward Declarations.
+struct AllocEntry;
class Pointers;
-constexpr size_t ACTION_MEMORY_SIZE = 128;
-
class Thread {
public:
Thread();
@@ -36,13 +34,13 @@ class Thread {
void SetPending();
void ClearPending();
- Action* CreateAction(uintptr_t key_pointer, const char* type, const char* line);
void AddTimeNsecs(uint64_t nsecs) { total_time_nsecs_ += nsecs; }
void set_pointers(Pointers* pointers) { pointers_ = pointers; }
Pointers* pointers() { return pointers_; }
- Action* GetAction() { return reinterpret_cast<Action*>(action_memory_); }
+ void SetAllocEntry(const AllocEntry* entry) { entry_ = entry; }
+ const AllocEntry& GetAllocEntry() { return *entry_; }
private:
pthread_mutex_t mutex_ = PTHREAD_MUTEX_INITIALIZER;
@@ -55,12 +53,7 @@ class Thread {
Pointers* pointers_ = nullptr;
- // Per thread memory for an Action. Only one action can be processed.
- // at a time.
- static constexpr size_t ACTION_SIZE = 128;
- uint8_t action_memory_[ACTION_SIZE];
+ const AllocEntry* entry_;
friend class Threads;
};
-
-#endif // _MEMORY_REPLAY_THREAD_H
diff --git a/memory_replay/Threads.cpp b/memory_replay/Threads.cpp
index 03c5d9cb..61f950ee 100644
--- a/memory_replay/Threads.cpp
+++ b/memory_replay/Threads.cpp
@@ -26,7 +26,8 @@
#include <new>
-#include "Action.h"
+#include "Alloc.h"
+#include "Pointers.h"
#include "Thread.h"
#include "Threads.h"
@@ -34,11 +35,11 @@ void* ThreadRunner(void* data) {
Thread* thread = reinterpret_cast<Thread*>(data);
while (true) {
thread->WaitForPending();
- Action* action = thread->GetAction();
- thread->AddTimeNsecs(action->Execute(thread->pointers()));
- bool end_thread = action->EndThread();
+ const AllocEntry& entry = thread->GetAllocEntry();
+ thread->AddTimeNsecs(AllocExecute(entry, thread->pointers()));
+ bool thread_done = entry.type == THREAD_DONE;
thread->ClearPending();
- if (end_thread) {
+ if (thread_done) {
break;
}
}
@@ -57,11 +58,6 @@ Threads::Threads(Pointers* pointers, size_t max_threads)
data_size_, max_threads_);
}
- if (Thread::ACTION_SIZE < Action::MaxActionSize()) {
- err(1, "Thread action size is too small: ACTION_SIZE %zu, max size %zu\n",
- Thread::ACTION_SIZE, Action::MaxActionSize());
- }
-
threads_ = new (memory) Thread[max_threads_];
}
@@ -149,9 +145,10 @@ void Threads::Finish(Thread* thread) {
}
void Threads::FinishAll() {
+ AllocEntry thread_done = {.type = THREAD_DONE};
for (size_t i = 0; i < max_threads_; i++) {
if (threads_[i].tid_ != 0) {
- threads_[i].CreateAction(0, "thread_done", nullptr);
+ threads_[i].SetAllocEntry(&thread_done);
threads_[i].SetPending();
Finish(threads_ + i);
}
diff --git a/memory_replay/Threads.h b/memory_replay/Threads.h
index 4778bff3..3bb4a320 100644
--- a/memory_replay/Threads.h
+++ b/memory_replay/Threads.h
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-#ifndef _MEMORY_REPLAY_THREADS_H
-#define _MEMORY_REPLAY_THREADS_H
+#pragma once
#include <stdint.h>
#include <sys/types.h>
+// Forward Declarations.
class Pointers;
class Thread;
@@ -53,5 +53,3 @@ class Threads {
friend Thread;
};
-
-#endif // _MEMORY_REPLAY_THREADS_H
diff --git a/memory_replay/TraceBenchmark.cpp b/memory_replay/TraceBenchmark.cpp
index 38eb69ae..b8f2bc8b 100644
--- a/memory_replay/TraceBenchmark.cpp
+++ b/memory_replay/TraceBenchmark.cpp
@@ -22,11 +22,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <time.h>
#include <unistd.h>
#include <algorithm>
-#include <memory>
#include <stack>
#include <string>
#include <unordered_map>
@@ -35,62 +33,39 @@
#include <android-base/file.h>
#include <android-base/strings.h>
#include <benchmark/benchmark.h>
-#include <ziparchive/zip_archive.h>
-
-enum AllocEnum : uint8_t {
- MALLOC = 0, // arg2 not used
- CALLOC, // size = item_count, arg2 = item_size
- MEMALIGN, // arg2 = alignment
- REALLOC, // if arg2 = 0, ptr arg is nullptr, else arg2 = old pointer index + 1
- FREE, // size not used, arg2 not used
-};
-struct MallocEntry {
- MallocEntry(AllocEnum type, size_t idx, size_t size, size_t arg2)
- : type(type), idx(idx), size(size), arg2(arg2) {}
+#include "Alloc.h"
+#include "Utils.h"
+#include "Zip.h"
+
+struct TraceAllocEntry {
+ TraceAllocEntry(AllocEnum type, size_t idx, size_t size, size_t last_arg)
+ : type(type), idx(idx), size(size) {
+ u.old_idx = last_arg;
+ }
AllocEnum type;
size_t idx;
size_t size;
- size_t arg2;
+ union {
+ size_t old_idx = 0;
+ size_t align;
+ size_t n_elements;
+ } u;
};
-static std::string GetZipContents(const char* filename) {
- ZipArchiveHandle archive;
- if (OpenArchive(filename, &archive) != 0) {
- return "";
- }
-
- std::string contents;
- void* cookie;
- if (StartIteration(archive, &cookie) == 0) {
- ZipEntry entry;
- std::string name;
- if (Next(cookie, &entry, &name) == 0) {
- contents.resize(entry.uncompressed_length);
- if (ExtractToMemory(archive, &entry, reinterpret_cast<uint8_t*>(contents.data()),
- entry.uncompressed_length) != 0) {
- contents = "";
- }
- }
- }
-
- CloseArchive(archive);
- return contents;
-}
-
-static size_t GetIndex(std::stack<size_t>& indices, size_t* max_index) {
- if (indices.empty()) {
+static size_t GetIndex(std::stack<size_t>& free_indices, size_t* max_index) {
+ if (free_indices.empty()) {
return (*max_index)++;
}
- size_t index = indices.top();
- indices.pop();
+ size_t index = free_indices.top();
+ free_indices.pop();
return index;
}
-static std::vector<MallocEntry>* GetTraceData(const char* filename, size_t* max_ptrs) {
+static std::vector<TraceAllocEntry>* GetTraceData(const char* filename, size_t* max_ptrs) {
// Only keep last trace encountered cached.
static std::string cached_filename;
- static std::vector<MallocEntry> cached_entries;
+ static std::vector<TraceAllocEntry> cached_entries;
static size_t cached_max_ptrs;
if (cached_filename == filename) {
@@ -102,7 +77,7 @@ static std::vector<MallocEntry>* GetTraceData(const char* filename, size_t* max_
cached_max_ptrs = 0;
cached_filename = filename;
- std::string content(GetZipContents(filename));
+ std::string content(ZipGetContents(filename));
if (content.empty()) {
errx(1, "Internal Error: Empty zip file %s", filename);
}
@@ -110,94 +85,64 @@ static std::vector<MallocEntry>* GetTraceData(const char* filename, size_t* max_
*max_ptrs = 0;
std::stack<size_t> free_indices;
- std::unordered_map<uintptr_t, size_t> indices;
- std::vector<MallocEntry>* entries = &cached_entries;
+ std::unordered_map<uint64_t, size_t> ptr_to_index;
+ std::vector<TraceAllocEntry>* entries = &cached_entries;
for (const std::string& line : lines) {
if (line.empty()) {
continue;
}
- pid_t tid;
- int line_pos = 0;
- char name[128];
- uintptr_t pointer;
- // All lines have this format:
- // TID: ALLOCATION_TYPE POINTER
- // 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
- if (sscanf(line.c_str(), "%d: %127s %" SCNxPTR " %n", &tid, name, &pointer, &line_pos) != 3) {
- errx(1, "Internal Error: Failed to process %s", line.c_str());
- }
- const char* line_end = &line[line_pos];
- std::string type(name);
- if (type == "malloc") {
- // Format:
- // TID: malloc POINTER SIZE_OF_ALLOCATION
- size_t size;
- if (sscanf(line_end, "%zu", &size) != 1) {
- errx(1, "Internal Error: Failed to read malloc data %s", line.c_str());
- }
- size_t idx = GetIndex(free_indices, max_ptrs);
- indices[pointer] = idx;
- entries->emplace_back(MALLOC, idx, size, 0);
- } else if (type == "free") {
- // Format:
- // TID: free POINTER
- if (pointer != 0) {
- auto entry = indices.find(pointer);
- if (entry == indices.end()) {
- errx(1, "Internal Error: Unable to find free pointer %" PRIuPTR, pointer);
- }
- free_indices.push(entry->second);
- entries->emplace_back(FREE, entry->second + 1, 0, 0);
- } else {
- entries->emplace_back(FREE, 0, 0, 0);
+ AllocEntry entry;
+ AllocGetData(line, &entry);
+
+ switch (entry.type) {
+ case MALLOC: {
+ size_t idx = GetIndex(free_indices, max_ptrs);
+ ptr_to_index[entry.ptr] = idx;
+ entries->emplace_back(MALLOC, idx, entry.size, 0);
+ break;
}
- } else if (type == "calloc") {
- // Format:
- // TID: calloc POINTER ITEM_SIZE ITEM_COUNT
- size_t n_elements;
- size_t size;
- if (sscanf(line_end, "%zu %zu", &n_elements, &size) != 2) {
- errx(1, "Internal Error: Failed to read calloc data %s", line.c_str());
+ case CALLOC: {
+ size_t idx = GetIndex(free_indices, max_ptrs);
+ ptr_to_index[entry.ptr] = idx;
+ entries->emplace_back(CALLOC, idx, entry.u.n_elements, entry.size);
+ break;
}
- size_t idx = GetIndex(free_indices, max_ptrs);
- indices[pointer] = idx;
- entries->emplace_back(CALLOC, idx, size, n_elements);
- } else if (type == "realloc") {
- // Format:
- // TID: calloc POINTER NEW_SIZE OLD_POINTER
- uintptr_t old_pointer;
- size_t size;
- if (sscanf(line_end, "%" SCNxPTR " %zu", &old_pointer, &size) != 2) {
- errx(1, "Internal Error: Failed to read realloc data %s", line.c_str());
+ case MEMALIGN: {
+ size_t idx = GetIndex(free_indices, max_ptrs);
+ ptr_to_index[entry.ptr] = idx;
+ entries->emplace_back(MEMALIGN, idx, entry.size, entry.u.align);
+ break;
}
- size_t old_pointer_idx = 0;
- if (old_pointer != 0) {
- auto entry = indices.find(old_pointer);
- if (entry == indices.end()) {
- errx(1, "Internal Error: Failed to find realloc pointer %" PRIuPTR, old_pointer);
+ case REALLOC: {
+ size_t old_pointer_idx = 0;
+ if (entry.u.old_ptr != 0) {
+ auto idx_entry = ptr_to_index.find(entry.u.old_ptr);
+ if (idx_entry == ptr_to_index.end()) {
+ errx(1, "File Error: Failed to find realloc pointer %" PRIu64, entry.u.old_ptr);
+ }
+ old_pointer_idx = idx_entry->second;
+ free_indices.push(old_pointer_idx);
}
- old_pointer_idx = entry->second;
- free_indices.push(old_pointer_idx);
- }
- size_t idx = GetIndex(free_indices, max_ptrs);
- indices[pointer] = idx;
- entries->emplace_back(REALLOC, idx, size, old_pointer_idx + 1);
- } else if (type == "memalign") {
- // Format:
- // TID: memalign POINTER SIZE ALIGNMENT
- size_t align;
- size_t size;
- if (sscanf(line_end, "%zu %zu", &align, &size) != 2) {
- errx(1, "Internal Error: Failed to read memalign data %s", line.c_str());
+ size_t idx = GetIndex(free_indices, max_ptrs);
+ ptr_to_index[entry.ptr] = idx;
+ entries->emplace_back(REALLOC, idx, entry.size, old_pointer_idx + 1);
+ break;
}
- size_t idx = GetIndex(free_indices, max_ptrs);
- indices[pointer] = idx;
- entries->emplace_back(MEMALIGN, idx, size, align);
- } else if (type != "thread_done") {
- errx(1, "Internal Error: Unknown type %s", line.c_str());
+ case FREE:
+ if (entry.ptr != 0) {
+ auto idx_entry = ptr_to_index.find(entry.ptr);
+ if (idx_entry == ptr_to_index.end()) {
+ errx(1, "File Error: Unable to find free pointer %" PRIu64, entry.ptr);
+ }
+ free_indices.push(idx_entry->second);
+ entries->emplace_back(FREE, idx_entry->second + 1, 0, 0);
+ } else {
+ entries->emplace_back(FREE, 0, 0, 0);
+ }
+ break;
+ case THREAD_DONE:
+ // Ignore these.
+ break;
}
}
@@ -205,21 +150,8 @@ static std::vector<MallocEntry>* GetTraceData(const char* filename, size_t* max_
return entries;
}
-static __always_inline uint64_t Nanotime() {
- struct timespec t;
- t.tv_sec = t.tv_nsec = 0;
- clock_gettime(CLOCK_MONOTONIC, &t);
- return static_cast<uint64_t>(t.tv_sec) * 1000000000LL + t.tv_nsec;
-}
-
-static __always_inline void MakeAllocationResident(void* ptr, size_t nbytes, int pagesize) {
- uint8_t* data = reinterpret_cast<uint8_t*>(ptr);
- for (size_t i = 0; i < nbytes; i += pagesize) {
- data[i] = 1;
- }
-}
-
-static void RunTrace(benchmark::State& state, std::vector<MallocEntry>& entries, size_t max_ptrs) {
+static void RunTrace(benchmark::State& state, std::vector<TraceAllocEntry>& entries,
+ size_t max_ptrs) {
std::vector<void*> ptrs(max_ptrs, nullptr);
int pagesize = getpagesize();
@@ -245,7 +177,7 @@ static void RunTrace(benchmark::State& state, std::vector<MallocEntry>& entries,
case CALLOC:
start_ns = Nanotime();
- ptr = calloc(entry.arg2, entry.size);
+ ptr = calloc(entry.u.n_elements, entry.size);
if (ptr == nullptr) {
errx(1, "calloc returned nullptr");
}
@@ -260,7 +192,7 @@ static void RunTrace(benchmark::State& state, std::vector<MallocEntry>& entries,
case MEMALIGN:
start_ns = Nanotime();
- ptr = memalign(entry.arg2, entry.size);
+ ptr = memalign(entry.u.align, entry.size);
if (ptr == nullptr) {
errx(1, "memalign returned nullptr");
}
@@ -275,11 +207,11 @@ static void RunTrace(benchmark::State& state, std::vector<MallocEntry>& entries,
case REALLOC:
start_ns = Nanotime();
- if (entry.arg2 == 0) {
+ if (entry.u.old_idx == 0) {
ptr = realloc(nullptr, entry.size);
} else {
- ptr = realloc(ptrs[entry.arg2 - 1], entry.size);
- ptrs[entry.arg2 - 1] = nullptr;
+ ptr = realloc(ptrs[entry.u.old_idx - 1], entry.size);
+ ptrs[entry.u.old_idx - 1] = nullptr;
}
if (entry.size > 0) {
if (ptr == nullptr) {
@@ -306,6 +238,9 @@ static void RunTrace(benchmark::State& state, std::vector<MallocEntry>& entries,
free(ptr);
total_ns += Nanotime() - start_ns;
break;
+
+ case THREAD_DONE:
+ break;
}
}
state.SetIterationTime(total_ns / double(1000000000.0));
@@ -319,7 +254,7 @@ static void RunTrace(benchmark::State& state, std::vector<MallocEntry>& entries,
static void BenchmarkTrace(benchmark::State& state, const char* filename) {
std::string full_filename(android::base::GetExecutableDirectory() + "/traces/" + filename);
size_t max_ptrs;
- std::vector<MallocEntry>* entries = GetTraceData(full_filename.c_str(), &max_ptrs);
+ std::vector<TraceAllocEntry>* entries = GetTraceData(full_filename.c_str(), &max_ptrs);
if (entries == nullptr) {
errx(1, "ERROR: Failed to get trace data for %s.", full_filename.c_str());
}
diff --git a/memory_replay/Utils.h b/memory_replay/Utils.h
new file mode 100644
index 00000000..44ce93ac
--- /dev/null
+++ b/memory_replay/Utils.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <time.h>
+
+static __always_inline uint64_t Nanotime() {
+ struct timespec t = {};
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return static_cast<uint64_t>(t.tv_sec) * 1000000000LL + t.tv_nsec;
+}
+
+static __always_inline void MakeAllocationResident(void* ptr, size_t nbytes, int pagesize) {
+ uint8_t* data = reinterpret_cast<uint8_t*>(ptr);
+ for (size_t i = 0; i < nbytes; i += pagesize) {
+ data[i] = 1;
+ }
+}
diff --git a/memory_replay/Zip.cpp b/memory_replay/Zip.cpp
new file mode 100644
index 00000000..8e2edf0a
--- /dev/null
+++ b/memory_replay/Zip.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2019 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 <err.h>
+#include <errno.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <ziparchive/zip_archive.h>
+
+#include "Alloc.h"
+#include "Zip.h"
+
+std::string ZipGetContents(const char* filename) {
+ ZipArchiveHandle archive;
+ if (OpenArchive(filename, &archive) != 0) {
+ return "";
+ }
+
+ // It is assumed that the archive contains only a single entry.
+ void* cookie;
+ std::string contents;
+ if (StartIteration(archive, &cookie) == 0) {
+ ZipEntry entry;
+ std::string name;
+ if (Next(cookie, &entry, &name) == 0) {
+ contents.resize(entry.uncompressed_length);
+ if (ExtractToMemory(archive, &entry, reinterpret_cast<uint8_t*>(contents.data()),
+ entry.uncompressed_length) != 0) {
+ contents = "";
+ }
+ }
+ }
+
+ CloseArchive(archive);
+ return contents;
+}
+
+void WaitPid(pid_t pid) {
+ int wstatus;
+ pid_t wait_pid = TEMP_FAILURE_RETRY(waitpid(pid, &wstatus, 0));
+ if (wait_pid != pid) {
+ if (wait_pid == -1) {
+ err(1, "waitpid() failed");
+ } else {
+ errx(1, "Unexpected pid from waitpid(): expected %d, returned %d", pid, wait_pid);
+ }
+ }
+ if (!WIFEXITED(wstatus)) {
+ errx(1, "Forked process did not terminate with exit() call");
+ }
+ if (WEXITSTATUS(wstatus) != 0) {
+ errx(1, "Bad exit value from forked process: returned %d", WEXITSTATUS(wstatus));
+ }
+}
+
+// This function should not do any memory allocations in the main function.
+// Any true allocation should happen in fork'd code.
+void ZipGetUnwindInfo(const char* filename, AllocEntry** entries, size_t* num_entries) {
+ void* mem =
+ mmap(nullptr, sizeof(size_t), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+ if (mem == MAP_FAILED) {
+ err(1, "Unable to allocate a shared map of size %zu", sizeof(size_t));
+ }
+ *reinterpret_cast<size_t*>(mem) = 0;
+
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ // First get the number of lines in the trace file. It is assumed
+ // that there are no blank lines, and every line contains a valid
+ // allocation operation.
+ std::string contents = ZipGetContents(filename);
+ if (contents.empty()) {
+ errx(1, "Unable to get contents of %s", filename);
+ }
+ size_t lines = 0;
+ size_t index = 0;
+ while (true) {
+ index = contents.find('\n', index);
+ if (index == std::string::npos) {
+ break;
+ }
+ index++;
+ lines++;
+ }
+ if (contents[contents.size() - 1] != '\n') {
+ // Add one since the last line doesn't end in '\n'.
+ lines++;
+ }
+ *reinterpret_cast<size_t*>(mem) = lines;
+ _exit(0);
+ } else if (pid == -1) {
+ err(1, "fork() call failed");
+ }
+ WaitPid(pid);
+ *num_entries = *reinterpret_cast<size_t*>(mem);
+ munmap(mem, sizeof(size_t));
+
+ mem = mmap(nullptr, *num_entries * sizeof(AllocEntry), PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+ if (mem == MAP_FAILED) {
+ err(1, "Unable to allocate a shared map of size %zu", *num_entries * sizeof(AllocEntry));
+ }
+ *entries = reinterpret_cast<AllocEntry*>(mem);
+
+ if ((pid = fork()) == 0) {
+ std::string contents = ZipGetContents(filename);
+ if (contents.empty()) {
+ errx(1, "Contents of zip file %s is empty.", filename);
+ }
+ size_t entry_idx = 0;
+ size_t start_str = 0;
+ size_t end_str = 0;
+ while (true) {
+ end_str = contents.find('\n', start_str);
+ if (end_str == std::string::npos) {
+ break;
+ }
+ if (entry_idx == *num_entries) {
+ errx(1, "Too many entries, stopped at entry %zu", entry_idx);
+ }
+ contents[end_str] = '\0';
+ AllocGetData(&contents[start_str], &(*entries)[entry_idx++]);
+ start_str = end_str + 1;
+ }
+ if (entry_idx != *num_entries) {
+ errx(1, "Mismatched number of entries found: expected %zu, found %zu", *num_entries,
+ entry_idx);
+ }
+ _exit(0);
+ } else if (pid == -1) {
+ err(1, "fork() call failed");
+ }
+ WaitPid(pid);
+}
+
+void ZipFreeEntries(AllocEntry* entries, size_t num_entries) {
+ munmap(entries, num_entries * sizeof(AllocEntry));
+}
diff --git a/memory_replay/LineBuffer.h b/memory_replay/Zip.h
index 934d3021..6224e86b 100644
--- a/memory_replay/LineBuffer.h
+++ b/memory_replay/Zip.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -14,23 +14,17 @@
* limitations under the License.
*/
-#ifndef _MEMORY_REPLAY_LINE_BUFFER_H
-#define _MEMORY_REPLAY_LINE_BUFFER_H
+#pragma once
#include <stdint.h>
-class LineBuffer {
- public:
- LineBuffer(int fd, char* buffer, size_t buffer_len);
+#include <string>
- bool GetLine(char** line, size_t* line_len);
+// Forward Declarations.
+struct AllocEntry;
- private:
- int fd_;
- char* buffer_ = nullptr;
- size_t buffer_len_ = 0;
- size_t start_ = 0;
- size_t bytes_ = 0;
-};
+std::string ZipGetContents(const char* filename);
-#endif // _MEMORY_REPLAY_LINE_BUFFER_H
+void ZipGetUnwindInfo(const char* filename, AllocEntry** entries, size_t* num_entries);
+
+void ZipFreeEntries(AllocEntry* entries, size_t num_entries);
diff --git a/memory_replay/main.cpp b/memory_replay/main.cpp
index 2ffc53d0..ff8c1afa 100644
--- a/memory_replay/main.cpp
+++ b/memory_replay/main.cpp
@@ -27,96 +27,67 @@
#include <sys/types.h>
#include <unistd.h>
-#include "Action.h"
-#include "LineBuffer.h"
+#include "Alloc.h"
#include "NativeInfo.h"
#include "Pointers.h"
#include "Thread.h"
#include "Threads.h"
+#include "Zip.h"
-static char g_buffer[65535];
+constexpr size_t DEFAULT_MAX_THREADS = 512;
-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;
+static size_t GetMaxAllocs(const AllocEntry* entries, size_t num_entries) {
size_t num_allocs = 0;
- 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) {
+ 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:
num_allocs--;
- }
- } else if (*word != 't') {
- // Skip the thread_done message.
- num_allocs++;
+ break;
}
}
return num_allocs;
}
-void ProcessDump(int fd, size_t max_allocs, size_t max_threads) {
- lseek(fd, 0, SEEK_SET);
+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);
Pointers pointers(max_allocs);
Threads threads(&pointers, max_threads);
- 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);
- }
- line_number++;
- if ((line_number % 100000) == 0) {
- printf(" At line %zu:\n", line_number);
- PrintNativeInfo(" ");
+ 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(" ");
}
- Thread* thread = threads.FindThread(tid);
+ const AllocEntry& entry = entries[i];
+ Thread* thread = threads.FindThread(entry.tid);
if (thread == nullptr) {
- thread = threads.CreateThread(tid);
+ thread = threads.CreateThread(entry.tid);
}
// Wait for the thread to complete any previous actions before handling
// the next action.
thread->WaitForReady();
- Action* action = thread->CreateAction(key_pointer, type, line + line_pos);
- if (action == nullptr) {
- err(1, "Cannot create action from line: %s\n", line);
- }
+ thread->SetAllocEntry(&entry);
- bool does_free = action->DoesFree();
+ bool does_free = AllocDoesFree(entry);
if (does_free) {
// Make sure that any other threads doing allocations are complete
// before triggering the action. Otherwise, another thread could
@@ -127,7 +98,7 @@ void ProcessDump(int fd, size_t max_allocs, size_t max_threads) {
// Tell the thread to execute the action.
thread->SetPending();
- if (action->EndThread()) {
+ if (entries[i].type == THREAD_DONE) {
// Wait for the thread to finish and clear the thread entry.
threads.Finish(thread);
}
@@ -142,7 +113,7 @@ void ProcessDump(int fd, size_t max_allocs, size_t max_threads) {
// Wait for all threads to stop processing actions.
threads.WaitForAllToQuiesce();
- PrintNativeInfo("Final ");
+ NativePrintInfo("Final ");
// Free any outstanding pointers.
// This allows us to run a tool like valgrind to verify that no memory
@@ -151,12 +122,12 @@ void ProcessDump(int fd, size_t max_allocs, size_t max_threads) {
pointers.FreeAll();
// Print out the total time making all allocation calls.
- printf("Total Allocation/Free Time: %" PRIu64 "ns %0.2fs\n", threads.total_time_nsecs(),
- threads.total_time_nsecs() / 1000000000.0);
+ 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);
}
-constexpr size_t DEFAULT_MAX_THREADS = 512;
-
int main(int argc, char** argv) {
if (argc != 2 && argc != 3) {
if (argc > 3) {
@@ -168,8 +139,14 @@ int main(int argc, char** argv) {
return 1;
}
+#if defined(__LP64__)
+ NativePrintf("64 bit environment.\n");
+#else
+ NativePrintf("32 bit environment.\n");
+#endif
+
#if defined(__BIONIC__)
- printf("Setting decay time to 1\n");
+ NativePrintf("Setting decay time to 1\n");
mallopt(M_DECAY_TIME, 1);
#endif
@@ -178,21 +155,15 @@ int main(int argc, char** argv) {
max_threads = atoi(argv[2]);
}
- 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;
- }
+ AllocEntry* entries;
+ size_t num_entries;
+ ZipGetUnwindInfo(argv[1], &entries, &num_entries);
- printf("Processing: %s\n", argv[1]);
+ NativePrintf("Processing: %s\n", argv[1]);
- // 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);
+ ProcessDump(entries, num_entries, max_threads);
- close(dump_fd);
+ ZipFreeEntries(entries, num_entries);
return 0;
}
diff --git a/memory_replay/tests/ActionTest.cpp b/memory_replay/tests/ActionTest.cpp
deleted file mode 100644
index cd72c24e..00000000
--- a/memory_replay/tests/ActionTest.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2015 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 <gtest/gtest.h>
-#include <stdint.h>
-#include <string.h>
-
-#include "Action.h"
-#include "Pointers.h"
-
-TEST(ActionTest, malloc) {
- uint8_t memory[Action::MaxActionSize()];
- const char* line = "1024";
- Action* action = Action::CreateAction(0x1234, "malloc", line, memory);
- ASSERT_TRUE(action != NULL);
- ASSERT_FALSE(action->DoesFree());
- ASSERT_FALSE(action->EndThread());
-
- Pointers pointers(1);
- action->Execute(&pointers);
- void* pointer = pointers.Remove(0x1234);
- ASSERT_TRUE(pointer != nullptr);
- free(pointer);
-}
-
-TEST(ActionTest, malloc_malformed) {
- uint8_t memory[128];
- const char* line = "";
- Action* action = Action::CreateAction(0x1234, "malloc", line, memory);
- ASSERT_FALSE(action != NULL);
-}
-
-TEST(ActionTest, free) {
- uint8_t memory[128];
- const char* line = "";
- Action* action = Action::CreateAction(0x1234, "free", line, memory);
- ASSERT_TRUE(action != NULL);
- ASSERT_TRUE(action->DoesFree());
- ASSERT_FALSE(action->EndThread());
-
- Pointers pointers(1);
- pointers.Add(0x1234, malloc(10));
- action->Execute(&pointers);
-}
-
-TEST(ActionTest, calloc) {
- uint8_t memory[128];
- const char* line = "100 10";
- Action* action = Action::CreateAction(0x1234, "calloc", line, memory);
- ASSERT_TRUE(action != NULL);
- ASSERT_FALSE(action->DoesFree());
- ASSERT_FALSE(action->EndThread());
-
- Pointers pointers(1);
- action->Execute(&pointers);
- void* pointer = pointers.Remove(0x1234);
- ASSERT_TRUE(pointer != nullptr);
- free(pointer);
-}
-
-TEST(ActionTest, free_zero) {
- uint8_t memory[128];
- const char* line = "";
- Action* action = Action::CreateAction(0, "free", line, memory);
- ASSERT_TRUE(action != NULL);
- ASSERT_FALSE(action->DoesFree());
- ASSERT_FALSE(action->EndThread());
- // Should be a nop.
- action->Execute(nullptr);
-}
-
-TEST(ActionTest, calloc_malformed) {
- uint8_t memory[128];
- const char* line1 = "100";
- Action* action = Action::CreateAction(0x1234, "calloc", line1, memory);
- ASSERT_FALSE(action != NULL);
-
- const char* line2 = "";
- action = Action::CreateAction(0x1234, "calloc", line2, memory);
- ASSERT_FALSE(action != NULL);
-}
-
-TEST(ActionTest, realloc) {
- uint8_t memory[128];
- const char* line = "0xabcd 100";
- Action* action = Action::CreateAction(0x1234, "realloc", line, memory);
- ASSERT_TRUE(action != NULL);
- ASSERT_TRUE(action->DoesFree());
- ASSERT_FALSE(action->EndThread());
-
- Pointers pointers(1);
- pointers.Add(0xabcd, malloc(10));
- action->Execute(&pointers);
- void* pointer = pointers.Remove(0x1234);
- ASSERT_TRUE(pointer != nullptr);
- free(pointer);
-
- const char* null_line = "0x0 100";
- action = Action::CreateAction(0x1234, "realloc", null_line, memory);
- ASSERT_FALSE(action->DoesFree());
- ASSERT_FALSE(action->EndThread());
-
- action->Execute(&pointers);
- pointer = pointers.Remove(0x1234);
- ASSERT_TRUE(pointer != nullptr);
- free(pointer);
-}
-
-TEST(ActionTest, realloc_malformed) {
- uint8_t memory[128];
- const char* line1 = "0x100";
- Action* action = Action::CreateAction(0x1234, "realloc", line1, memory);
- ASSERT_FALSE(action != NULL);
-
- const char* line2 = "";
- action = Action::CreateAction(0x1234, "realloc", line2, memory);
- ASSERT_FALSE(action != NULL);
-}
-
-TEST(ActionTest, memalign) {
- uint8_t memory[128];
- const char* line = "16 300";
- Action* action = Action::CreateAction(0x1234, "memalign", line, memory);
- ASSERT_TRUE(action != NULL);
- ASSERT_FALSE(action->DoesFree());
- ASSERT_FALSE(action->EndThread());
-
- Pointers pointers(1);
- action->Execute(&pointers);
- void* pointer = pointers.Remove(0x1234);
- ASSERT_TRUE(pointer != nullptr);
- free(pointer);
-}
-
-TEST(ActionTest, memalign_malformed) {
- uint8_t memory[128];
- const char* line1 = "100";
- Action* action = Action::CreateAction(0x1234, "memalign", line1, memory);
- ASSERT_FALSE(action != NULL);
-
- const char* line2 = "";
- action = Action::CreateAction(0x1234, "memalign", line2, memory);
- ASSERT_FALSE(action != NULL);
-}
-
-TEST(ActionTest, endthread) {
- uint8_t memory[128];
- const char* line = "";
- Action* action = Action::CreateAction(0x0, "thread_done", line, memory);
- ASSERT_TRUE(action != NULL);
- ASSERT_FALSE(action->DoesFree());
- ASSERT_TRUE(action->EndThread());
-
- action->Execute(nullptr);
-}
diff --git a/memory_replay/tests/AllocTest.cpp b/memory_replay/tests/AllocTest.cpp
new file mode 100644
index 00000000..d5dd0573
--- /dev/null
+++ b/memory_replay/tests/AllocTest.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2019 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 <stdint.h>
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include "Alloc.h"
+
+TEST(AllocTest, malloc_valid) {
+ std::string line = "1234: malloc 0xabd0000 20";
+ AllocEntry entry;
+ AllocGetData(line, &entry);
+ EXPECT_EQ(MALLOC, entry.type);
+ EXPECT_EQ(1234, entry.tid);
+ EXPECT_EQ(0xabd0000U, entry.ptr);
+ EXPECT_EQ(20U, entry.size);
+ EXPECT_EQ(0U, entry.u.align);
+}
+
+TEST(AllocTest, malloc_invalid) {
+ std::string line = "1234: malloc 0xabd0000";
+ AllocEntry entry;
+ EXPECT_DEATH(AllocGetData(line, &entry), "");
+
+ line = "1234: malloc";
+ EXPECT_DEATH(AllocGetData(line, &entry), "");
+}
+
+TEST(AllocTest, free_valid) {
+ std::string line = "1235: free 0x5000";
+ AllocEntry entry;
+ AllocGetData(line, &entry);
+ EXPECT_EQ(FREE, entry.type);
+ EXPECT_EQ(1235, entry.tid);
+ EXPECT_EQ(0x5000U, entry.ptr);
+ EXPECT_EQ(0U, entry.size);
+ EXPECT_EQ(0U, entry.u.align);
+}
+
+TEST(AllocTest, free_invalid) {
+ std::string line = "1234: free";
+ AllocEntry entry;
+ EXPECT_DEATH(AllocGetData(line, &entry), "");
+}
+
+TEST(AllocTest, calloc_valid) {
+ std::string line = "1236: calloc 0x8000 50 30";
+ AllocEntry entry;
+ AllocGetData(line, &entry);
+ EXPECT_EQ(CALLOC, entry.type);
+ EXPECT_EQ(1236, entry.tid);
+ EXPECT_EQ(0x8000U, entry.ptr);
+ EXPECT_EQ(30U, entry.size);
+ EXPECT_EQ(50U, entry.u.n_elements);
+}
+
+TEST(AllocTest, calloc_invalid) {
+ std::string line = "1236: calloc 0x8000 50";
+ AllocEntry entry;
+ EXPECT_DEATH(AllocGetData(line, &entry), "");
+
+ line = "1236: calloc 0x8000";
+ EXPECT_DEATH(AllocGetData(line, &entry), "");
+
+ line = "1236: calloc";
+ EXPECT_DEATH(AllocGetData(line, &entry), "");
+}
+
+TEST(AllocTest, realloc_valid) {
+ std::string line = "1237: realloc 0x9000 0x4000 80";
+ AllocEntry entry;
+ AllocGetData(line, &entry);
+ EXPECT_EQ(REALLOC, entry.type);
+ EXPECT_EQ(1237, entry.tid);
+ EXPECT_EQ(0x9000U, entry.ptr);
+ EXPECT_EQ(80U, entry.size);
+ EXPECT_EQ(0x4000U, entry.u.old_ptr);
+}
+
+TEST(AllocTest, realloc_invalid) {
+ std::string line = "1237: realloc 0x9000 0x4000";
+ AllocEntry entry;
+ EXPECT_DEATH(AllocGetData(line, &entry), "");
+
+ line = "1237: realloc 0x9000";
+ EXPECT_DEATH(AllocGetData(line, &entry), "");
+
+ line = "1237: realloc";
+ EXPECT_DEATH(AllocGetData(line, &entry), "");
+}
+
+TEST(AllocTest, memalign_valid) {
+ std::string line = "1238: memalign 0xa000 16 89";
+ AllocEntry entry;
+ AllocGetData(line, &entry);
+ EXPECT_EQ(MEMALIGN, entry.type);
+ EXPECT_EQ(1238, entry.tid);
+ EXPECT_EQ(0xa000U, entry.ptr);
+ EXPECT_EQ(89U, entry.size);
+ EXPECT_EQ(16U, entry.u.align);
+}
+
+TEST(AllocTest, memalign_invalid) {
+ std::string line = "1238: memalign 0xa000 16";
+ AllocEntry entry;
+ EXPECT_DEATH(AllocGetData(line, &entry), "");
+
+ line = "1238: memalign 0xa000";
+ EXPECT_DEATH(AllocGetData(line, &entry), "");
+
+ line = "1238: memalign";
+ EXPECT_DEATH(AllocGetData(line, &entry), "");
+}
+
+TEST(AllocTest, thread_done_valid) {
+ std::string line = "1239: thread_done 0x0";
+ AllocEntry entry;
+ AllocGetData(line, &entry);
+ EXPECT_EQ(THREAD_DONE, entry.type);
+ EXPECT_EQ(1239, entry.tid);
+ EXPECT_EQ(0U, entry.ptr);
+ EXPECT_EQ(0U, entry.size);
+ EXPECT_EQ(0U, entry.u.old_ptr);
+}
+
+TEST(AllocTest, thread_done_invalid) {
+ std::string line = "1240: thread_done";
+ AllocEntry entry;
+ EXPECT_DEATH(AllocGetData(line, &entry), "");
+}
diff --git a/memory_replay/tests/LineBufferTest.cpp b/memory_replay/tests/LineBufferTest.cpp
deleted file mode 100644
index 1a310226..00000000
--- a/memory_replay/tests/LineBufferTest.cpp
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright (C) 2015 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 <gtest/gtest.h>
-
-#include <string>
-
-#include <android-base/file.h>
-
-#include "LineBuffer.h"
-
-class LineBufferTest : public ::testing::Test {
- protected:
- void SetUp() override {
- tmp_file_ = new TemporaryFile();
- ASSERT_TRUE(tmp_file_->fd != -1);
- }
-
- void TearDown() override {
- delete tmp_file_;
- }
-
- TemporaryFile* tmp_file_ = nullptr;
-};
-
-TEST_F(LineBufferTest, single_line) {
- std::string line_data;
- line_data += "Single line with newline.\n";
- ASSERT_TRUE(TEMP_FAILURE_RETRY(
- write(tmp_file_->fd, line_data.c_str(), line_data.size())) != -1);
- ASSERT_TRUE(lseek(tmp_file_->fd, 0, SEEK_SET) != off_t(-1));
-
- char buffer[100];
- LineBuffer line_buf(tmp_file_->fd, buffer, sizeof(buffer));
-
- char* line;
- size_t line_len;
- ASSERT_TRUE(line_buf.GetLine(&line, &line_len));
- ASSERT_STREQ("Single line with newline.", line);
- ASSERT_EQ(sizeof("Single line with newline.") - 1, line_len);
-
- ASSERT_FALSE(line_buf.GetLine(&line, &line_len));
-}
-
-TEST_F(LineBufferTest, single_line_no_newline) {
- std::string line_data;
- line_data += "Single line with no newline.";
- ASSERT_TRUE(TEMP_FAILURE_RETRY(
- write(tmp_file_->fd, line_data.c_str(), line_data.size())) != -1);
- ASSERT_TRUE(lseek(tmp_file_->fd, 0, SEEK_SET) != off_t(-1));
-
- char buffer[100];
- LineBuffer line_buf(tmp_file_->fd, buffer, sizeof(buffer));
-
- char* line;
- size_t line_len;
- ASSERT_TRUE(line_buf.GetLine(&line, &line_len));
- ASSERT_STREQ("Single line with no newline.", line);
- ASSERT_EQ(sizeof("Single line with no newline.") - 1, line_len);
-
- ASSERT_FALSE(line_buf.GetLine(&line, &line_len));
-}
-
-TEST_F(LineBufferTest, single_read) {
- std::string line_data;
- line_data += "The first line.\n";
- line_data += "Second line here.\n";
- line_data += "Third line is last.\n";
- ASSERT_TRUE(TEMP_FAILURE_RETRY(
- write(tmp_file_->fd, line_data.c_str(), line_data.size())) != -1);
- ASSERT_TRUE(lseek(tmp_file_->fd, 0, SEEK_SET) != off_t(-1));
-
- char buffer[100];
- LineBuffer line_buf(tmp_file_->fd, buffer, sizeof(buffer));
-
- char* line;
- size_t line_len;
- ASSERT_TRUE(line_buf.GetLine(&line, &line_len));
- ASSERT_STREQ("The first line.", line);
- ASSERT_EQ(sizeof("The first line.") - 1, line_len);
-
- ASSERT_TRUE(line_buf.GetLine(&line, &line_len));
- ASSERT_STREQ("Second line here.", line);
- ASSERT_EQ(sizeof("Second line here.") - 1, line_len);
-
- ASSERT_TRUE(line_buf.GetLine(&line, &line_len));
- ASSERT_STREQ("Third line is last.", line);
- ASSERT_EQ(sizeof("Third line is last.") - 1, line_len);
-
- ASSERT_FALSE(line_buf.GetLine(&line, &line_len));
-}
-
-TEST_F(LineBufferTest, single_read_no_end_newline) {
- std::string line_data;
- line_data += "The first line.\n";
- line_data += "Second line here.\n";
- line_data += "Third line is last no newline.";
- ASSERT_TRUE(TEMP_FAILURE_RETRY(
- write(tmp_file_->fd, line_data.c_str(), line_data.size())) != -1);
- ASSERT_TRUE(lseek(tmp_file_->fd, 0, SEEK_SET) != off_t(-1));
-
- char buffer[100];
- LineBuffer line_buf(tmp_file_->fd, buffer, sizeof(buffer));
-
- char* line;
- size_t line_len;
- ASSERT_TRUE(line_buf.GetLine(&line, &line_len));
- ASSERT_STREQ("The first line.", line);
- ASSERT_EQ(sizeof("The first line.") - 1, line_len);
-
- ASSERT_TRUE(line_buf.GetLine(&line, &line_len));
- ASSERT_STREQ("Second line here.", line);
- ASSERT_EQ(sizeof("Second line here.") - 1, line_len);
-
- ASSERT_TRUE(line_buf.GetLine(&line, &line_len));
- ASSERT_STREQ("Third line is last no newline.", line);
- ASSERT_EQ(sizeof("Third line is last no newline.") - 1, line_len);
-
- ASSERT_FALSE(line_buf.GetLine(&line, &line_len));
-}
-
-TEST_F(LineBufferTest, one_line_per_read) {
- std::string line_data;
- line_data += "The first line.\n";
- line_data += "Second line here.\n";
- line_data += "Third line is last.\n";
- line_data += "The fourth line.\n";
- ASSERT_TRUE(TEMP_FAILURE_RETRY(
- write(tmp_file_->fd, line_data.c_str(), line_data.size())) != -1);
- ASSERT_TRUE(lseek(tmp_file_->fd, 0, SEEK_SET) != off_t(-1));
-
- char buffer[24];
- LineBuffer line_buf(tmp_file_->fd, buffer, sizeof(buffer));
-
- char* line;
- size_t line_len;
- ASSERT_TRUE(line_buf.GetLine(&line, &line_len));
- ASSERT_STREQ("The first line.", line);
- ASSERT_EQ(sizeof("The first line.") - 1, line_len);
-
- ASSERT_TRUE(line_buf.GetLine(&line, &line_len));
- ASSERT_STREQ("Second line here.", line);
- ASSERT_EQ(sizeof("Second line here.") - 1, line_len);
-
- ASSERT_TRUE(line_buf.GetLine(&line, &line_len));
- ASSERT_STREQ("Third line is last.", line);
- ASSERT_EQ(sizeof("Third line is last.") - 1, line_len);
-
- line_data += "The fourth line.\n";
- ASSERT_TRUE(line_buf.GetLine(&line, &line_len));
- ASSERT_STREQ("The fourth line.", line);
- ASSERT_EQ(sizeof("The fourth line.") - 1, line_len);
-
- ASSERT_FALSE(line_buf.GetLine(&line, &line_len));
-}
-
-TEST_F(LineBufferTest, multiple_line_per_read_multiple_reads) {
- std::string line_data;
- line_data += "The first line.\n";
- line_data += "Second line here.\n";
- line_data += "Third line is last.\n";
- line_data += "The fourth line.\n";
- ASSERT_TRUE(TEMP_FAILURE_RETRY(
- write(tmp_file_->fd, line_data.c_str(), line_data.size())) != -1);
- ASSERT_TRUE(lseek(tmp_file_->fd, 0, SEEK_SET) != off_t(-1));
-
- char buffer[60];
- LineBuffer line_buf(tmp_file_->fd, buffer, sizeof(buffer));
-
- char* line;
- size_t line_len;
- ASSERT_TRUE(line_buf.GetLine(&line, &line_len));
- ASSERT_STREQ("The first line.", line);
- ASSERT_EQ(sizeof("The first line.") - 1, line_len);
-
- ASSERT_TRUE(line_buf.GetLine(&line, &line_len));
- ASSERT_STREQ("Second line here.", line);
- ASSERT_EQ(sizeof("Second line here.") - 1, line_len);
-
- ASSERT_TRUE(line_buf.GetLine(&line, &line_len));
- ASSERT_STREQ("Third line is last.", line);
- ASSERT_EQ(sizeof("Third line is last.") - 1, line_len);
-
- line_data += "The fourth line.\n";
- ASSERT_TRUE(line_buf.GetLine(&line, &line_len));
- ASSERT_STREQ("The fourth line.", line);
- ASSERT_EQ(sizeof("The fourth line.") - 1, line_len);
-
- ASSERT_FALSE(line_buf.GetLine(&line, &line_len));
-}
-
-TEST_F(LineBufferTest, line_larger_than_buffer) {
- std::string line_data;
- line_data += "The first line.\n";
- line_data += "Second line here.\n";
- line_data += "This is a really, really, really, kind of long.\n";
- line_data += "The fourth line.\n";
- ASSERT_TRUE(TEMP_FAILURE_RETRY(
- write(tmp_file_->fd, line_data.c_str(), line_data.size())) != -1);
- ASSERT_TRUE(lseek(tmp_file_->fd, 0, SEEK_SET) != off_t(-1));
-
- char buffer[25];
- LineBuffer line_buf(tmp_file_->fd, buffer, sizeof(buffer));
-
- char* line;
- size_t line_len;
- ASSERT_TRUE(line_buf.GetLine(&line, &line_len));
- ASSERT_STREQ("The first line.", line);
- ASSERT_EQ(sizeof("The first line.") - 1, line_len);
-
- ASSERT_TRUE(line_buf.GetLine(&line, &line_len));
- ASSERT_STREQ("Second line here.", line);
- ASSERT_EQ(sizeof("Second line here.") - 1, line_len);
-
- ASSERT_TRUE(line_buf.GetLine(&line, &line_len));
- ASSERT_STREQ("This is a really, really", line);
- ASSERT_EQ(sizeof(buffer) - 1, line_len);
- ASSERT_TRUE(line_buf.GetLine(&line, &line_len));
- ASSERT_STREQ(", really, kind of long.", line);
- ASSERT_EQ(sizeof(", really, kind of long.") - 1, line_len);
-
- line_data += "The fourth line.\n";
- ASSERT_TRUE(line_buf.GetLine(&line, &line_len));
- ASSERT_STREQ("The fourth line.", line);
- ASSERT_EQ(sizeof("The fourth line.") - 1, line_len);
-
- ASSERT_FALSE(line_buf.GetLine(&line, &line_len));
-}
diff --git a/memory_replay/tests/NativeInfoTest.cpp b/memory_replay/tests/NativeInfoTest.cpp
index 44c87704..e09f0658 100644
--- a/memory_replay/tests/NativeInfoTest.cpp
+++ b/memory_replay/tests/NativeInfoTest.cpp
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-#include <gtest/gtest.h>
#include <stdint.h>
#include <string>
#include <android-base/file.h>
+#include <gtest/gtest.h>
#include "NativeInfo.h"
@@ -61,7 +61,7 @@ TEST_F(NativeInfoTest, no_matching) {
size_t rss_bytes = 1;
size_t va_bytes = 1;
- GetNativeInfo(tmp_file_->fd, &rss_bytes, &va_bytes);
+ NativeGetInfo(tmp_file_->fd, &rss_bytes, &va_bytes);
ASSERT_EQ(0U, rss_bytes);
ASSERT_EQ(0U, va_bytes);
}
@@ -122,7 +122,7 @@ TEST_F(NativeInfoTest, multiple_anons) {
size_t rss_bytes = 1;
size_t va_bytes = 1;
- GetNativeInfo(tmp_file_->fd, &rss_bytes, &va_bytes);
+ NativeGetInfo(tmp_file_->fd, &rss_bytes, &va_bytes);
ASSERT_EQ(32768U, rss_bytes);
ASSERT_EQ(12288U, va_bytes);
}
@@ -183,7 +183,7 @@ TEST_F(NativeInfoTest, multiple_heaps) {
size_t rss_bytes = 1;
size_t va_bytes = 1;
- GetNativeInfo(tmp_file_->fd, &rss_bytes, &va_bytes);
+ NativeGetInfo(tmp_file_->fd, &rss_bytes, &va_bytes);
ASSERT_EQ(45056U, rss_bytes);
ASSERT_EQ(12288U, va_bytes);
}
@@ -260,7 +260,7 @@ TEST_F(NativeInfoTest, mix_heap_anon) {
size_t rss_bytes = 1;
size_t va_bytes = 1;
- GetNativeInfo(tmp_file_->fd, &rss_bytes, &va_bytes);
+ NativeGetInfo(tmp_file_->fd, &rss_bytes, &va_bytes);
ASSERT_EQ(73728U, rss_bytes);
ASSERT_EQ(12288U, va_bytes);
}
diff --git a/memory_replay/tests/ThreadTest.cpp b/memory_replay/tests/ThreadTest.cpp
index 72492905..4cecf189 100644
--- a/memory_replay/tests/ThreadTest.cpp
+++ b/memory_replay/tests/ThreadTest.cpp
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-#include <gtest/gtest.h>
#include <pthread.h>
#include <unistd.h>
#include <utility>
-#include "Action.h"
+#include <gtest/gtest.h>
+
#include "Pointers.h"
#include "Thread.h"
@@ -99,16 +99,3 @@ TEST(ThreadTest, pointers) {
thread.set_pointers(&pointers);
ASSERT_TRUE(thread.pointers() == &pointers);
}
-
-TEST(ThreadTest, action) {
- Thread thread;
-
- Action* action = thread.CreateAction(0x1234, "thread_done", "");
- ASSERT_EQ(action, thread.GetAction());
-
- // Verify the action object is not garbage.
- action->Execute(nullptr);
-
- ASSERT_TRUE(action->EndThread());
- ASSERT_FALSE(action->DoesFree());
-}
diff --git a/memory_replay/tests/ThreadsTest.cpp b/memory_replay/tests/ThreadsTest.cpp
index c2ba023c..990c9130 100644
--- a/memory_replay/tests/ThreadsTest.cpp
+++ b/memory_replay/tests/ThreadsTest.cpp
@@ -16,7 +16,7 @@
#include <gtest/gtest.h>
-#include "Action.h"
+#include "Alloc.h"
#include "Pointers.h"
#include "Thread.h"
#include "Threads.h"
@@ -32,7 +32,8 @@ TEST(ThreadsTest, single_thread) {
Thread* found_thread = threads.FindThread(900);
ASSERT_EQ(thread, found_thread);
- thread->CreateAction(0x1234, "thread_done", "");
+ AllocEntry thread_done = {.type = THREAD_DONE};
+ thread->SetAllocEntry(&thread_done);
thread->SetPending();
@@ -66,9 +67,10 @@ TEST(ThreadsTest, multiple_threads) {
Thread* found_thread3 = threads.FindThread(902);
ASSERT_EQ(thread3, found_thread3);
- thread1->CreateAction(0x1234, "thread_done", "");
- thread2->CreateAction(0x1235, "thread_done", "");
- thread3->CreateAction(0x1236, "thread_done", "");
+ AllocEntry thread_done = {.type = THREAD_DONE};
+ thread1->SetAllocEntry(&thread_done);
+ thread2->SetAllocEntry(&thread_done);
+ thread3->SetAllocEntry(&thread_done);
thread1->SetPending();
threads.Finish(thread1);
@@ -93,17 +95,26 @@ TEST(ThreadsTest, verify_quiesce) {
// If WaitForAllToQuiesce is not correct, then this should provoke an error
// since we are overwriting the action data while it's being used.
- for (size_t i = 0; i < 512; i++) {
- thread->CreateAction(0x1234 + i, "malloc", "100");
+ constexpr size_t kAllocEntries = 512;
+ std::vector<AllocEntry> mallocs(kAllocEntries);
+ std::vector<AllocEntry> frees(kAllocEntries);
+ for (size_t i = 0; i < kAllocEntries; i++) {
+ mallocs[i].type = MALLOC;
+ mallocs[i].ptr = 0x1234 + i;
+ mallocs[i].size = 100;
+ thread->SetAllocEntry(&mallocs[i]);
thread->SetPending();
threads.WaitForAllToQuiesce();
- thread->CreateAction(0x1234 + i, "free", "");
+ frees[i].type = FREE;
+ frees[i].ptr = 0x1234 + i;
+ thread->SetAllocEntry(&frees[i]);
thread->SetPending();
threads.WaitForAllToQuiesce();
}
- thread->CreateAction(0x1236, "thread_done", "");
+ AllocEntry thread_done = {.type = THREAD_DONE};
+ thread->SetAllocEntry(&thread_done);
thread->SetPending();
threads.Finish(thread);
ASSERT_EQ(0U, threads.num_threads());
diff --git a/memory_replay/tests/ZipTest.cpp b/memory_replay/tests/ZipTest.cpp
new file mode 100644
index 00000000..e5302d55
--- /dev/null
+++ b/memory_replay/tests/ZipTest.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 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 <malloc.h>
+#include <stdint.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+
+#include "Alloc.h"
+#include "Zip.h"
+
+std::string GetTestZip() {
+ return android::base::GetExecutableDirectory() + "/tests/test.zip";
+}
+
+TEST(ZipTest, zip_get_contents) {
+ EXPECT_EQ("12345: malloc 0x1000 16\n12345: free 0x1000\n", ZipGetContents(GetTestZip().c_str()));
+}
+
+TEST(ZipTest, zip_get_contents_bad_file) {
+ EXPECT_EQ("", ZipGetContents("/does/not/exist"));
+}
+
+TEST(ZipTest, zip_get_unwind_info) {
+ // This will allocate, so do it before getting mallinfo.
+ std::string file_name = GetTestZip();
+
+ size_t mallinfo_before = mallinfo().uordblks;
+ AllocEntry* entries;
+ size_t num_entries;
+ ZipGetUnwindInfo(file_name.c_str(), &entries, &num_entries);
+ size_t mallinfo_after = mallinfo().uordblks;
+
+ // Verify no memory is allocated.
+ EXPECT_EQ(mallinfo_after, mallinfo_before);
+
+ ASSERT_EQ(2U, num_entries);
+ EXPECT_EQ(12345, entries[0].tid);
+ EXPECT_EQ(MALLOC, entries[0].type);
+ EXPECT_EQ(0x1000U, entries[0].ptr);
+ EXPECT_EQ(16U, entries[0].size);
+ EXPECT_EQ(0U, entries[0].u.old_ptr);
+
+ EXPECT_EQ(12345, entries[1].tid);
+ EXPECT_EQ(FREE, entries[1].type);
+ EXPECT_EQ(0x1000U, entries[1].ptr);
+ EXPECT_EQ(0U, entries[1].size);
+ EXPECT_EQ(0U, entries[1].u.old_ptr);
+
+ ZipFreeEntries(entries, num_entries);
+}
+
+TEST(ZipTest, zip_get_unwind_info_bad_file) {
+ AllocEntry* entries;
+ size_t num_entries;
+ EXPECT_DEATH(ZipGetUnwindInfo("/does/not/exist", &entries, &num_entries), "");
+}
diff --git a/memory_replay/tests/test.zip b/memory_replay/tests/test.zip
new file mode 100644
index 00000000..baef5597
--- /dev/null
+++ b/memory_replay/tests/test.zip
Binary files differ
diff --git a/memory_replay/traces/README b/memory_replay/traces/README
index d306b9a4..88b7b59b 100644
--- a/memory_replay/traces/README
+++ b/memory_replay/traces/README
@@ -11,7 +11,7 @@ Format of dumps:
<tid>
The pid_t value that is the gettid() value recorded during the run.
-<action_name>
+<action_name>
One of:
malloc - Allocate memory using the malloc function.
calloc - Allocate memory using the calloc function.