aboutsummaryrefslogtreecommitdiff
path: root/src/client/linux
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/linux')
-rw-r--r--src/client/linux/handler/exception_handler.cc28
-rw-r--r--src/client/linux/handler/exception_handler.h11
-rw-r--r--src/client/linux/handler/exception_handler_unittest.cc81
-rw-r--r--src/client/linux/minidump_writer/minidump_writer.cc58
-rw-r--r--src/client/linux/minidump_writer/minidump_writer.h26
-rw-r--r--src/client/linux/minidump_writer/minidump_writer_unittest.cc79
6 files changed, 265 insertions, 18 deletions
diff --git a/src/client/linux/handler/exception_handler.cc b/src/client/linux/handler/exception_handler.cc
index 490b6eb9..6f0b8de4 100644
--- a/src/client/linux/handler/exception_handler.cc
+++ b/src/client/linux/handler/exception_handler.cc
@@ -495,13 +495,15 @@ bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
crashing_process,
context,
context_size,
- mapping_list_);
+ mapping_list_,
+ app_memory_list_);
}
return google_breakpad::WriteMinidump(minidump_descriptor_.path(),
crashing_process,
context,
context_size,
- mapping_list_);
+ mapping_list_,
+ app_memory_list_);
}
// static
@@ -562,4 +564,26 @@ void ExceptionHandler::AddMappingInfo(const string& name,
mapping_list_.push_back(mapping);
}
+void ExceptionHandler::RegisterAppMemory(void* ptr, size_t length) {
+ AppMemoryList::iterator iter =
+ std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr);
+ if (iter != app_memory_list_.end()) {
+ // Don't allow registering the same pointer twice.
+ return;
+ }
+
+ AppMemory app_memory;
+ app_memory.ptr = ptr;
+ app_memory.length = length;
+ app_memory_list_.push_back(app_memory);
+}
+
+void ExceptionHandler::UnregisterAppMemory(void* ptr) {
+ AppMemoryList::iterator iter =
+ std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr);
+ if (iter != app_memory_list_.end()) {
+ app_memory_list_.erase(iter);
+ }
+}
+
} // namespace google_breakpad
diff --git a/src/client/linux/handler/exception_handler.h b/src/client/linux/handler/exception_handler.h
index 95817424..68953adf 100644
--- a/src/client/linux/handler/exception_handler.h
+++ b/src/client/linux/handler/exception_handler.h
@@ -193,6 +193,13 @@ class ExceptionHandler {
size_t mapping_size,
size_t file_offset);
+ // Register a block of memory of length bytes starting at address ptr
+ // to be copied to the minidump when a crash happens.
+ void RegisterAppMemory(void* ptr, size_t length);
+
+ // Unregister a block of memory that was registered with RegisterAppMemory.
+ void UnregisterAppMemory(void* ptr);
+
// Force signal handling for the specified signal.
bool SimulateSignalDelivery(int sig);
private:
@@ -238,6 +245,10 @@ class ExceptionHandler {
// Callers can add extra info about mappings for cases where the
// dumper code cannot extract enough information from /proc/<pid>/maps.
MappingList mapping_list_;
+
+ // Callers can request additional memory regions to be included in
+ // the dump.
+ AppMemoryList app_memory_list_;
};
} // namespace google_breakpad
diff --git a/src/client/linux/handler/exception_handler_unittest.cc b/src/client/linux/handler/exception_handler_unittest.cc
index 6de9b8d4..341d2fb0 100644
--- a/src/client/linux/handler/exception_handler_unittest.cc
+++ b/src/client/linux/handler/exception_handler_unittest.cc
@@ -939,6 +939,85 @@ TEST(ExceptionHandlerTest, GenerateMultipleDumpsWithPath) {
ASSERT_TRUE(minidump2.Read());
unlink(minidump_2.path());
- // We expect 2 distinct files.
+ // 2 distinct files should be produced.
ASSERT_STRNE(minidump_1_path.c_str(), minidump_2_path.c_str());
}
+
+// Test that an additional memory region can be added to the minidump.
+TEST(ExceptionHandlerTest, AdditionalMemory) {
+ const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE);
+
+ // Get some heap memory.
+ u_int8_t* memory = new u_int8_t[kMemorySize];
+ const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
+ ASSERT_TRUE(memory);
+
+ // Stick some data into the memory so the contents can be verified.
+ for (u_int32_t i = 0; i < kMemorySize; ++i) {
+ memory[i] = i % 255;
+ }
+
+ AutoTempDir temp_dir;
+ ExceptionHandler handler(
+ MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1);
+
+ // Add the memory region to the list of memory to be included.
+ handler.RegisterAppMemory(memory, kMemorySize);
+ handler.WriteMinidump();
+
+ const MinidumpDescriptor& minidump_desc = handler.minidump_descriptor();
+
+ // Read the minidump. Ensure that the memory region is present
+ Minidump minidump(minidump_desc.path());
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(dump_memory_list);
+ const MinidumpMemoryRegion* region =
+ dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress);
+ ASSERT_TRUE(region);
+
+ EXPECT_EQ(kMemoryAddress, region->GetBase());
+ EXPECT_EQ(kMemorySize, region->GetSize());
+
+ // Verify memory contents.
+ EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize));
+
+ delete[] memory;
+}
+
+// Test that a memory region that was previously registered
+// can be unregistered.
+TEST(ExceptionHandlerTest, AdditionalMemoryRemove) {
+ const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE);
+
+ // Get some heap memory.
+ u_int8_t* memory = new u_int8_t[kMemorySize];
+ const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
+ ASSERT_TRUE(memory);
+
+ AutoTempDir temp_dir;
+ ExceptionHandler handler(
+ MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1);
+
+ // Add the memory region to the list of memory to be included.
+ handler.RegisterAppMemory(memory, kMemorySize);
+
+ // ...and then remove it
+ handler.UnregisterAppMemory(memory);
+ handler.WriteMinidump();
+
+ const MinidumpDescriptor& minidump_desc = handler.minidump_descriptor();
+
+ // Read the minidump. Ensure that the memory region is not present.
+ Minidump minidump(minidump_desc.path());
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(dump_memory_list);
+ const MinidumpMemoryRegion* region =
+ dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress);
+ EXPECT_FALSE(region);
+
+ delete[] memory;
+}
diff --git a/src/client/linux/minidump_writer/minidump_writer.cc b/src/client/linux/minidump_writer/minidump_writer.cc
index eb2e6d64..b081b298 100644
--- a/src/client/linux/minidump_writer/minidump_writer.cc
+++ b/src/client/linux/minidump_writer/minidump_writer.cc
@@ -76,6 +76,7 @@
namespace {
+using google_breakpad::AppMemoryList;
using google_breakpad::ExceptionHandler;
using google_breakpad::LineReader;
using google_breakpad::LinuxDumper;
@@ -381,6 +382,7 @@ class MinidumpWriter {
int minidump_fd,
const ExceptionHandler::CrashContext* context,
const MappingList& mappings,
+ const AppMemoryList& appmem,
LinuxDumper* dumper)
: fd_(minidump_fd),
path_(minidump_path),
@@ -393,7 +395,8 @@ class MinidumpWriter {
#endif
dumper_(dumper),
memory_blocks_(dumper_->allocator()),
- mapping_list_(mappings) {
+ mapping_list_(mappings),
+ app_memory_list_(appmem) {
// Assert there should be either a valid fd or a valid path, not both.
assert(fd_ != -1 || minidump_path);
assert(fd_ == -1 || !minidump_path);
@@ -480,6 +483,9 @@ class MinidumpWriter {
return false;
dir.CopyIndex(dir_index++, &dirent);
+ if (!WriteAppMemory())
+ return false;
+
if (!WriteMemoryListStream(&dirent))
return false;
dir.CopyIndex(dir_index++, &dirent);
@@ -781,6 +787,30 @@ class MinidumpWriter {
return true;
}
+ // Write application-provided memory regions.
+ bool WriteAppMemory() {
+ for (AppMemoryList::const_iterator iter = app_memory_list_.begin();
+ iter != app_memory_list_.end();
+ ++iter) {
+ uint8_t* data_copy =
+ reinterpret_cast<uint8_t*>(dumper_->allocator()->Alloc(iter->length));
+ dumper_->CopyFromProcess(data_copy, GetCrashThread(), iter->ptr,
+ iter->length);
+
+ UntypedMDRVA memory(&minidump_writer_);
+ if (!memory.Allocate(iter->length)) {
+ return false;
+ }
+ memory.Copy(data_copy, iter->length);
+ MDMemoryDescriptor desc;
+ desc.start_of_memory_range = reinterpret_cast<uintptr_t>(iter->ptr);
+ desc.memory = memory.location();
+ memory_blocks_.push_back(desc);
+ }
+
+ return true;
+ }
+
static bool ShouldIncludeMapping(const MappingInfo& mapping) {
if (mapping.name[0] == 0 || // only want modules with filenames.
mapping.offset || // only want to include one mapping per shared lib.
@@ -1361,6 +1391,9 @@ class MinidumpWriter {
wasteful_vector<MDMemoryDescriptor> memory_blocks_;
// Additional information about some mappings provided by the caller.
const MappingList& mapping_list_;
+ // Additional memory regions to be included in the dump,
+ // provided by the caller.
+ const AppMemoryList& app_memory_list_;
};
@@ -1368,7 +1401,8 @@ bool WriteMinidumpImpl(const char* minidump_path,
int minidump_fd,
pid_t crashing_process,
const void* blob, size_t blob_size,
- const MappingList& mappings) {
+ const MappingList& mappings,
+ const AppMemoryList& appmem) {
if (blob_size != sizeof(ExceptionHandler::CrashContext))
return false;
const ExceptionHandler::CrashContext* context =
@@ -1378,7 +1412,8 @@ bool WriteMinidumpImpl(const char* minidump_path,
reinterpret_cast<uintptr_t>(context->siginfo.si_addr));
dumper.set_crash_signal(context->siginfo.si_signo);
dumper.set_crash_thread(context->tid);
- MinidumpWriter writer(minidump_path, minidump_fd, context, mappings, &dumper);
+ MinidumpWriter writer(minidump_path, minidump_fd, context, mappings,
+ appmem, &dumper);
if (!writer.Init())
return false;
return writer.Dump();
@@ -1391,33 +1426,36 @@ namespace google_breakpad {
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
const void* blob, size_t blob_size) {
return WriteMinidumpImpl(minidump_path, -1, crashing_process, blob, blob_size,
- MappingList());
+ MappingList(), AppMemoryList());
}
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
const void* blob, size_t blob_size) {
return WriteMinidumpImpl(NULL, minidump_fd, crashing_process, blob, blob_size,
- MappingList());
+ MappingList(), AppMemoryList());
}
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
const void* blob, size_t blob_size,
- const MappingList& mappings) {
+ const MappingList& mappings,
+ const AppMemoryList& appmem) {
return WriteMinidumpImpl(minidump_path, -1, crashing_process, blob, blob_size,
- mappings);
+ mappings, appmem);
}
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
const void* blob, size_t blob_size,
- const MappingList& mappings) {
+ const MappingList& mappings,
+ const AppMemoryList& appmem) {
return WriteMinidumpImpl(NULL, minidump_fd, crashing_process, blob, blob_size,
- mappings);
+ mappings, appmem);
}
bool WriteMinidump(const char* filename,
const MappingList& mappings,
+ const AppMemoryList& appmem,
LinuxDumper* dumper) {
- MinidumpWriter writer(filename, -1, NULL, mappings, dumper);
+ MinidumpWriter writer(filename, -1, NULL, mappings, appmem, dumper);
if (!writer.Init())
return false;
return writer.Dump();
diff --git a/src/client/linux/minidump_writer/minidump_writer.h b/src/client/linux/minidump_writer/minidump_writer.h
index 14da94b6..1370d938 100644
--- a/src/client/linux/minidump_writer/minidump_writer.h
+++ b/src/client/linux/minidump_writer/minidump_writer.h
@@ -51,6 +51,22 @@ struct MappingEntry {
// A list of <MappingInfo, GUID>
typedef std::list<MappingEntry> MappingList;
+// These entries store a list of memory regions that the client wants included
+// in the minidump.
+struct AppMemory {
+ void* ptr;
+ size_t length;
+
+ bool operator==(const struct AppMemory& other) const {
+ return ptr == other.ptr;
+ }
+
+ bool operator==(const void* other) const {
+ return ptr == other;
+ }
+};
+typedef std::list<AppMemory> AppMemoryList;
+
// Writes a minidump to the filesystem. These functions do not malloc nor use
// libc functions which may. Thus, it can be used in contexts where the state
// of the heap may be corrupt.
@@ -67,16 +83,20 @@ bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
const void* blob, size_t blob_size);
-// These overloads also allow passing a list of known mappings.
+// These overloads also allow passing a list of known mappings and
+// a list of additional memory regions to be included in the minidump.
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
const void* blob, size_t blob_size,
- const MappingList& mappings);
+ const MappingList& mappings,
+ const AppMemoryList& appdata);
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
const void* blob, size_t blob_size,
- const MappingList& mappings);
+ const MappingList& mappings,
+ const AppMemoryList& appdata);
bool WriteMinidump(const char* filename,
const MappingList& mappings,
+ const AppMemoryList& appdata,
LinuxDumper* dumper);
} // namespace google_breakpad
diff --git a/src/client/linux/minidump_writer/minidump_writer_unittest.cc b/src/client/linux/minidump_writer/minidump_writer_unittest.cc
index 7558bee4..0a6b07d1 100644
--- a/src/client/linux/minidump_writer/minidump_writer_unittest.cc
+++ b/src/client/linux/minidump_writer/minidump_writer_unittest.cc
@@ -32,6 +32,7 @@
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
+#include <ucontext.h>
#include <unistd.h>
#include <string>
@@ -184,12 +185,13 @@ TEST(MinidumpWriterTest, MappingInfo) {
strcpy(info.name, kMemoryName);
MappingList mappings;
+ AppMemoryList memory_list;
MappingEntry mapping;
mapping.first = info;
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
mappings.push_back(mapping);
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
- mappings));
+ mappings, memory_list));
// Read the minidump. Load the module list, and ensure that
// the mmap'ed |memory| is listed with the given module name
@@ -288,12 +290,13 @@ TEST(MinidumpWriterTest, MappingInfoContained) {
strcpy(info.name, kMemoryName);
MappingList mappings;
+ AppMemoryList memory_list;
MappingEntry mapping;
mapping.first = info;
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
mappings.push_back(mapping);
ASSERT_TRUE(WriteMinidump(dumpfile.c_str(), child, &context, sizeof(context),
- mappings));
+ mappings, memory_list));
// Read the minidump. Load the module list, and ensure that
// the mmap'ed |memory| is listed with the given module name
@@ -410,3 +413,75 @@ TEST(MinidumpWriterTest, DeletedBinary) {
module_identifier += "0";
EXPECT_EQ(module_identifier, module->debug_identifier());
}
+
+// Test that an additional memory region can be added to the minidump.
+TEST(MinidumpWriterTest, AdditionalMemory) {
+ int fds[2];
+ ASSERT_NE(-1, pipe(fds));
+
+ // These are defined here so the parent can use them to check the
+ // data from the minidump afterwards.
+ const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE);
+
+ // Get some heap memory.
+ u_int8_t* memory = new u_int8_t[kMemorySize];
+ const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
+ ASSERT_TRUE(memory);
+
+ // Stick some data into the memory so the contents can be verified.
+ for (u_int32_t i = 0; i < kMemorySize; ++i) {
+ memory[i] = i % 255;
+ }
+
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[1]);
+ char b;
+ HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
+ close(fds[0]);
+ syscall(__NR_exit);
+ }
+ close(fds[0]);
+
+ ExceptionHandler::CrashContext context;
+
+ // This needs a valid context for minidump writing to work, but getting
+ // a useful one from the child is too much work, so just use one from
+ // the parent since the child is just a forked copy anyway.
+ ASSERT_EQ(0, getcontext(&context.context));
+ context.tid = child;
+
+ AutoTempDir temp_dir;
+ string templ = temp_dir.path() + "/minidump-writer-unittest";
+ unlink(templ.c_str());
+
+ MappingList mappings;
+ AppMemoryList memory_list;
+
+ // Add the memory region to the list of memory to be included.
+ AppMemory app_memory;
+ app_memory.ptr = memory;
+ app_memory.length = kMemorySize;
+ memory_list.push_back(app_memory);
+ ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
+ mappings, memory_list));
+
+ // Read the minidump. Ensure that the memory region is present
+ Minidump minidump(templ.c_str());
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(dump_memory_list);
+ const MinidumpMemoryRegion* region =
+ dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress);
+ ASSERT_TRUE(region);
+
+ EXPECT_EQ(kMemoryAddress, region->GetBase());
+ EXPECT_EQ(kMemorySize, region->GetSize());
+
+ // Verify memory contents.
+ EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize));
+
+ delete[] memory;
+ close(fds[1]);
+}