Modify CrashGenerator::CreateChildCrash to copy proc files.

This patch is taken from the downstream version of breakpad in
Chromium OS:  https://gerrit.chromium.org/gerrit/15148

LinuxCoreDumperTest previously assumes the proc files of the child
process created by CrashGenerator::CreateChildCrash() have the same
content as its parent process, which may not be true. This CL modifies
CrashGenerator to copy the proc files of the child process, created by
CreateChildCrash(), before crashing that process.

BUG=chromium-os:25252
TEST=Verified the following:
TEST=Tested the following:
1. Build on 32-bit and 64-bit Linux with gcc 4.4.3 and gcc 4.6.
2. Build on Mac OS X 10.6.8 with gcc 4.2 and clang 3.0 (with latest gmock).
3. All unit tests pass.
Review URL: https://breakpad.appspot.com/353001

git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@925 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
benchan@chromium.org 2012-02-23 18:50:08 +00:00
parent 7caf87236a
commit 907f95c5bd
5 changed files with 116 additions and 20 deletions

View file

@ -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());

View file

@ -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();

View file

@ -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);

View file

@ -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) {

View file

@ -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.