summaryrefslogtreecommitdiff
path: root/memory_replay
diff options
context:
space:
mode:
authorChristopher Ferris <cferris@google.com>2019-08-26 17:51:19 -0700
committerChristopher Ferris <cferris@google.com>2019-08-30 08:53:08 -0700
commit48d0744499477f371e0589ece5ad92976af29133 (patch)
tree2e09f06be81b031f85953be8192c0eddeefca3b2 /memory_replay
parentabde21870e073997a766055ad660dfd5a8f1fa22 (diff)
downloadextras-48d0744499477f371e0589ece5ad92976af29133.tar.gz
Refactor code with single trace parser.
This is the first step towards adding a new way to benchmark the traces. This new way would be a way to add all threads running at once. Also to try and make it possible to do the faster runs that jmgao had done a while ago. Modified the code so that it's possible for memory_replay to play the zip file, no need to unzip the trace files. Add a single parser for the trace files. Remove as many possible allocation points in the system. This allows the traces to be run as pure as possible. Added new unit tests to cover new code. Bug: 137795072 Test: All unit tests pass. Test: Ran select traces using the old version and the new version. Test: Both get nearlyt he same RSS, but slightly different VA space Test: due to the new version avoiding allocations at all costs. Test: Ran the trace benchmarks and verified that the old version and the Test: new version produce nearly the same numbers. Change-Id: I8e8a28436f23b09eba57772c62bb0b866d874437
Diffstat (limited to 'memory_replay')
-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.