aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/linux/minidump_writer/linux_core_dumper_unittest.cc18
-rw-r--r--src/common/linux/tests/crash_generator.cc38
-rw-r--r--src/common/linux/tests/crash_generator.h22
-rw-r--r--src/common/tests/file_utils.cc55
-rw-r--r--src/common/tests/file_utils.h3
5 files changed, 116 insertions, 20 deletions
diff --git a/src/client/linux/minidump_writer/linux_core_dumper_unittest.cc b/src/client/linux/minidump_writer/linux_core_dumper_unittest.cc
index e335413b..1fb26ede 100644
--- a/src/client/linux/minidump_writer/linux_core_dumper_unittest.cc
+++ b/src/client/linux/minidump_writer/linux_core_dumper_unittest.cc
@@ -63,7 +63,7 @@ TEST(LinuxCoreDumperTest, BuildProcPath) {
TEST(LinuxCoreDumperTest, VerifyDumpWithMultipleThreads) {
CrashGenerator crash_generator;
if (!crash_generator.HasDefaultCorePattern()) {
- fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test"
+ fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test "
"is skipped due to non-default core pattern\n");
return;
}
@@ -76,23 +76,15 @@ TEST(LinuxCoreDumperTest, VerifyDumpWithMultipleThreads) {
// CrashGenerator is identified and fixed.
if (!crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread,
kCrashSignal, &child_pid)) {
- fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test"
+ fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test "
"is skipped due to no core dump generated\n");
return;
}
pid_t pid = getpid();
- const char* core_file = crash_generator.GetCoreFilePath().c_str();
-
- // Since CrashGenerator::CreateChildCrash() simply crashed a fork of
- // this process, we expect that those proc files, which are used by
- // LinuxCoreDumper, of crashed child process have the same content of
- // this process. So we simply pass the proc files of this process to
- // LinuxCoreDumper.
- char procfs_path[NAME_MAX];
- snprintf(procfs_path, NAME_MAX, "/proc/%d", pid);
-
- LinuxCoreDumper dumper(child_pid, core_file, procfs_path);
+ const string core_file = crash_generator.GetCoreFilePath();
+ const string procfs_path = crash_generator.GetDirectoryOfProcFilesCopy();
+ LinuxCoreDumper dumper(child_pid, core_file.c_str(), procfs_path.c_str());
dumper.Init();
EXPECT_TRUE(dumper.IsPostMortem());
diff --git a/src/common/linux/tests/crash_generator.cc b/src/common/linux/tests/crash_generator.cc
index 7274acf9..dff2bca2 100644
--- a/src/common/linux/tests/crash_generator.cc
+++ b/src/common/linux/tests/crash_generator.cc
@@ -55,6 +55,12 @@ struct ThreadData {
pid_t* thread_id_ptr;
};
+const char* const kProcFilesToCopy[] = {
+ "auxv", "cmdline", "environ", "maps", "status"
+};
+const size_t kNumProcFilesToCopy =
+ sizeof(kProcFilesToCopy) / sizeof(kProcFilesToCopy[0]);
+
// Core file size limit set to 1 MB, which is big enough for test purposes.
const rlim_t kCoreSizeLimit = 1024 * 1024;
@@ -95,6 +101,10 @@ std::string CrashGenerator::GetCoreFilePath() const {
return temp_dir_.path() + "/core";
}
+std::string CrashGenerator::GetDirectoryOfProcFilesCopy() const {
+ return temp_dir_.path() + "/proc";
+}
+
pid_t CrashGenerator::GetThreadId(unsigned index) const {
return reinterpret_cast<pid_t*>(shared_memory_)[index];
}
@@ -160,6 +170,15 @@ bool CrashGenerator::CreateChildCrash(
}
if (SetCoreFileSizeLimit(kCoreSizeLimit)) {
CreateThreadsInChildProcess(num_threads);
+ std::string proc_dir = GetDirectoryOfProcFilesCopy();
+ if (mkdir(proc_dir.c_str(), 0755) == -1) {
+ perror("CrashGenerator: Failed to create proc directory");
+ exit(1);
+ }
+ if (!CopyProcFiles(getpid(), proc_dir.c_str())) {
+ fprintf(stderr, "CrashGenerator: Failed to copy proc files\n");
+ exit(1);
+ }
if (kill(*GetThreadIdPointer(crash_thread), crash_signal) == -1) {
perror("CrashGenerator: Failed to kill thread by signal");
}
@@ -185,6 +204,25 @@ bool CrashGenerator::CreateChildCrash(
return true;
}
+bool CrashGenerator::CopyProcFiles(pid_t pid, const char* path) const {
+ char from_path[PATH_MAX], to_path[PATH_MAX];
+ for (size_t i = 0; i < kNumProcFilesToCopy; ++i) {
+ int num_chars = snprintf(from_path, PATH_MAX, "/proc/%d/%s",
+ pid, kProcFilesToCopy[i]);
+ if (num_chars < 0 || num_chars >= PATH_MAX)
+ return false;
+
+ num_chars = snprintf(to_path, PATH_MAX, "%s/%s",
+ path, kProcFilesToCopy[i]);
+ if (num_chars < 0 || num_chars >= PATH_MAX)
+ return false;
+
+ if (!CopyFile(from_path, to_path))
+ return false;
+ }
+ return true;
+}
+
void CrashGenerator::CreateThreadsInChildProcess(unsigned num_threads) {
*GetThreadIdPointer(0) = getpid();
diff --git a/src/common/linux/tests/crash_generator.h b/src/common/linux/tests/crash_generator.h
index 874b15cd..d05ce73d 100644
--- a/src/common/linux/tests/crash_generator.h
+++ b/src/common/linux/tests/crash_generator.h
@@ -58,13 +58,12 @@ class CrashGenerator {
// if /proc/sys/kernel/core_pattern has the default value 'core'.
bool HasDefaultCorePattern() const;
- // Sets the maximum size of core dump file (both the soft and hard limit)
- // to |limit| bytes. Returns true on success.
- bool SetCoreFileSizeLimit(rlim_t limit) const;
-
// Returns the expected path of the core dump file.
std::string GetCoreFilePath() const;
+ // Returns the directory of a copy of proc files of the child process.
+ std::string GetDirectoryOfProcFilesCopy() const;
+
// Creates a crash (and a core dump file) by creating a child process with
// |num_threads| threads, and the terminating the child process by sending
// a signal with number |crash_signal| to the |crash_thread|-th thread.
@@ -72,14 +71,23 @@ class CrashGenerator {
bool CreateChildCrash(unsigned num_threads, unsigned crash_thread,
int crash_signal, pid_t* child_pid);
- // Creates |num_threads| threads in the child process.
- void CreateThreadsInChildProcess(unsigned num_threads);
-
// Returns the thread ID of the |index|-th thread in the child process.
// This method does not validate |index|.
pid_t GetThreadId(unsigned index) const;
private:
+ // Copies the following proc files of the process with |pid| to the directory
+ // at |path|: auxv, cmdline, environ, maps, status
+ // The directory must have been created. Returns true on success.
+ bool CopyProcFiles(pid_t pid, const char* path) const;
+
+ // Creates |num_threads| threads in the child process.
+ void CreateThreadsInChildProcess(unsigned num_threads);
+
+ // Sets the maximum size of core dump file (both the soft and hard limit)
+ // to |limit| bytes. Returns true on success.
+ bool SetCoreFileSizeLimit(rlim_t limit) const;
+
// Creates a shared memory of |memory_size| bytes for communicating thread
// IDs between the parent and child process. Returns true on success.
bool MapSharedMemory(size_t memory_size);
diff --git a/src/common/tests/file_utils.cc b/src/common/tests/file_utils.cc
index 0d9b04ad..1264b078 100644
--- a/src/common/tests/file_utils.cc
+++ b/src/common/tests/file_utils.cc
@@ -40,6 +40,61 @@
namespace google_breakpad {
+bool CopyFile(const char* from_path, const char* to_path) {
+ int infile = HANDLE_EINTR(open(from_path, O_RDONLY));
+ if (infile < 0) {
+ perror("open");
+ return false;
+ }
+
+ int outfile = HANDLE_EINTR(creat(to_path, 0666));
+ if (outfile < 0) {
+ perror("creat");
+ if (HANDLE_EINTR(close(infile)) < 0) {
+ perror("close");
+ }
+ return false;
+ }
+
+ char buffer[1024];
+ bool result = true;
+
+ while (result) {
+ ssize_t bytes_read = HANDLE_EINTR(read(infile, buffer, sizeof(buffer)));
+ if (bytes_read < 0) {
+ perror("read");
+ result = false;
+ break;
+ }
+ if (bytes_read == 0)
+ break;
+ ssize_t bytes_written_per_read = 0;
+ do {
+ ssize_t bytes_written_partial = HANDLE_EINTR(write(
+ outfile,
+ &buffer[bytes_written_per_read],
+ bytes_read - bytes_written_per_read));
+ if (bytes_written_partial < 0) {
+ perror("write");
+ result = false;
+ break;
+ }
+ bytes_written_per_read += bytes_written_partial;
+ } while (bytes_written_per_read < bytes_read);
+ }
+
+ if (HANDLE_EINTR(close(infile)) == -1) {
+ perror("close");
+ result = false;
+ }
+ if (HANDLE_EINTR(close(outfile)) == -1) {
+ perror("close");
+ result = false;
+ }
+
+ return result;
+}
+
bool ReadFile(const char* path, void* buffer, ssize_t* buffer_size) {
int fd = HANDLE_EINTR(open(path, O_RDONLY));
if (fd == -1) {
diff --git a/src/common/tests/file_utils.h b/src/common/tests/file_utils.h
index 52e8b009..c98a9bfa 100644
--- a/src/common/tests/file_utils.h
+++ b/src/common/tests/file_utils.h
@@ -35,6 +35,9 @@
namespace google_breakpad {
+// Copies a file from |from_path| to |to_path|. Returns true on success.
+bool CopyFile(const char* from_path, const char* to_path);
+
// Reads the content of a file at |path| into |buffer|. |buffer_size| specifies
// the size of |buffer| in bytes and returns the number of bytes read from the
// file on success. Returns true on success.