elf_core_dump_unittest.cc
1 // Copyright 2011 Google LLC 2 // 3 // Redistribution and use in source and binary forms, with or without 4 // modification, are permitted provided that the following conditions are 5 // met: 6 // 7 // * Redistributions of source code must retain the above copyright 8 // notice, this list of conditions and the following disclaimer. 9 // * Redistributions in binary form must reproduce the above 10 // copyright notice, this list of conditions and the following disclaimer 11 // in the documentation and/or other materials provided with the 12 // distribution. 13 // * Neither the name of Google LLC nor the names of its 14 // contributors may be used to endorse or promote products derived from 15 // this software without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 // elf_core_dump_unittest.cc: Unit tests for google_breakpad::ElfCoreDump. 30 31 #ifdef HAVE_CONFIG_H 32 #include <config.h> // Must come first 33 #endif 34 35 #include <sys/procfs.h> 36 37 #include <set> 38 #include <string> 39 40 #include "breakpad_googletest_includes.h" 41 #include "common/linux/elf_core_dump.h" 42 #include "common/linux/memory_mapped_file.h" 43 #include "common/tests/file_utils.h" 44 #include "common/linux/tests/crash_generator.h" 45 #include "common/using_std_string.h" 46 47 using google_breakpad::AutoTempDir; 48 using google_breakpad::CrashGenerator; 49 using google_breakpad::ElfCoreDump; 50 using google_breakpad::MemoryMappedFile; 51 using google_breakpad::MemoryRange; 52 using google_breakpad::WriteFile; 53 using std::set; 54 55 TEST(ElfCoreDumpTest, DefaultConstructor) { 56 ElfCoreDump core; 57 EXPECT_FALSE(core.IsValid()); 58 EXPECT_EQ(NULL, core.GetHeader()); 59 EXPECT_EQ(0U, core.GetProgramHeaderCount()); 60 EXPECT_EQ(NULL, core.GetProgramHeader(0)); 61 EXPECT_EQ(NULL, core.GetFirstProgramHeaderOfType(PT_LOAD)); 62 EXPECT_FALSE(core.GetFirstNote().IsValid()); 63 } 64 65 TEST(ElfCoreDumpTest, TestElfHeader) { 66 ElfCoreDump::Ehdr header; 67 memset(&header, 0, sizeof(header)); 68 69 AutoTempDir temp_dir; 70 string core_path = temp_dir.path() + "/core"; 71 const char* core_file = core_path.c_str(); 72 MemoryMappedFile mapped_core_file; 73 ElfCoreDump core; 74 75 ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header) - 1)); 76 ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); 77 core.SetContent(mapped_core_file.content()); 78 EXPECT_FALSE(core.IsValid()); 79 EXPECT_EQ(NULL, core.GetHeader()); 80 EXPECT_EQ(0U, core.GetProgramHeaderCount()); 81 EXPECT_EQ(NULL, core.GetProgramHeader(0)); 82 EXPECT_EQ(NULL, core.GetFirstProgramHeaderOfType(PT_LOAD)); 83 EXPECT_FALSE(core.GetFirstNote().IsValid()); 84 85 ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); 86 ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); 87 core.SetContent(mapped_core_file.content()); 88 EXPECT_FALSE(core.IsValid()); 89 90 header.e_ident[0] = ELFMAG0; 91 ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); 92 ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); 93 core.SetContent(mapped_core_file.content()); 94 EXPECT_FALSE(core.IsValid()); 95 96 header.e_ident[1] = ELFMAG1; 97 ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); 98 ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); 99 core.SetContent(mapped_core_file.content()); 100 EXPECT_FALSE(core.IsValid()); 101 102 header.e_ident[2] = ELFMAG2; 103 ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); 104 ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); 105 core.SetContent(mapped_core_file.content()); 106 EXPECT_FALSE(core.IsValid()); 107 108 header.e_ident[3] = ELFMAG3; 109 ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); 110 ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); 111 core.SetContent(mapped_core_file.content()); 112 EXPECT_FALSE(core.IsValid()); 113 114 header.e_ident[4] = ElfCoreDump::kClass; 115 ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); 116 ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); 117 core.SetContent(mapped_core_file.content()); 118 EXPECT_FALSE(core.IsValid()); 119 120 header.e_version = EV_CURRENT; 121 ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); 122 ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); 123 core.SetContent(mapped_core_file.content()); 124 EXPECT_FALSE(core.IsValid()); 125 126 header.e_type = ET_CORE; 127 ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); 128 ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); 129 core.SetContent(mapped_core_file.content()); 130 EXPECT_TRUE(core.IsValid()); 131 } 132 133 TEST(ElfCoreDumpTest, ValidCoreFile) { 134 CrashGenerator crash_generator; 135 if (!crash_generator.HasDefaultCorePattern()) { 136 GTEST_SKIP() << "ElfCoreDumpTest.ValidCoreFile test is skipped " 137 "due to non-default core pattern"; 138 } 139 140 if (!crash_generator.HasResourceLimitsAmenableToCrashCollection()) { 141 GTEST_SKIP() << "ElfCoreDumpTest.ValidCoreFile test is skipped " 142 "due to inadequate system resource limits"; 143 } 144 145 const unsigned kNumOfThreads = 3; 146 const unsigned kCrashThread = 1; 147 const int kCrashSignal = SIGABRT; 148 ASSERT_TRUE(crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread, 149 kCrashSignal, NULL)); 150 pid_t expected_crash_thread_id = crash_generator.GetThreadId(kCrashThread); 151 set<pid_t> expected_thread_ids; 152 for (unsigned i = 0; i < kNumOfThreads; ++i) { 153 expected_thread_ids.insert(crash_generator.GetThreadId(i)); 154 } 155 156 #if defined(__ANDROID__) 157 struct stat st; 158 if (stat(crash_generator.GetCoreFilePath().c_str(), &st) != 0) { 159 fprintf(stderr, "ElfCoreDumpTest.ValidCoreFile test is skipped " 160 "due to no core file being generated"); 161 return; 162 } 163 #endif 164 165 MemoryMappedFile mapped_core_file; 166 ASSERT_TRUE( 167 mapped_core_file.Map(crash_generator.GetCoreFilePath().c_str(), 0)); 168 169 ElfCoreDump core; 170 core.SetContent(mapped_core_file.content()); 171 EXPECT_TRUE(core.IsValid()); 172 173 // Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are 174 // ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific): 175 // Thread Name Type 176 // ------------------------------------------------------------------- 177 // 1st thread CORE NT_PRSTATUS 178 // process-wide CORE NT_PRPSINFO 179 // process-wide CORE NT_AUXV 180 // 1st thread CORE NT_FPREGSET 181 // 1st thread LINUX NT_PRXFPREG 182 // 1st thread LINUX NT_386_TLS 183 // 184 // 2nd thread CORE NT_PRSTATUS 185 // 2nd thread CORE NT_FPREGSET 186 // 2nd thread LINUX NT_PRXFPREG 187 // 2nd thread LINUX NT_386_TLS 188 // 189 // 3rd thread CORE NT_PRSTATUS 190 // 3rd thread CORE NT_FPREGSET 191 // 3rd thread LINUX NT_PRXFPREG 192 // 3rd thread LINUX NT_386_TLS 193 194 size_t num_nt_prpsinfo = 0; 195 size_t num_nt_prstatus = 0; 196 size_t num_pr_fpvalid = 0; 197 #if defined(__i386__) || defined(__x86_64__) 198 size_t num_nt_fpregset = 0; 199 #endif 200 #if defined(__i386__) 201 size_t num_nt_prxfpreg = 0; 202 #endif 203 set<pid_t> actual_thread_ids; 204 ElfCoreDump::Note note = core.GetFirstNote(); 205 while (note.IsValid()) { 206 MemoryRange name = note.GetName(); 207 MemoryRange description = note.GetDescription(); 208 EXPECT_FALSE(name.IsEmpty()); 209 EXPECT_FALSE(description.IsEmpty()); 210 211 switch (note.GetType()) { 212 case NT_PRPSINFO: { 213 EXPECT_TRUE(description.data() != NULL); 214 EXPECT_EQ(sizeof(elf_prpsinfo), description.length()); 215 ++num_nt_prpsinfo; 216 break; 217 } 218 case NT_PRSTATUS: { 219 EXPECT_TRUE(description.data() != NULL); 220 EXPECT_EQ(sizeof(elf_prstatus), description.length()); 221 const elf_prstatus* status = description.GetData<elf_prstatus>(0); 222 actual_thread_ids.insert(status->pr_pid); 223 if (num_nt_prstatus == 0) { 224 EXPECT_EQ(expected_crash_thread_id, status->pr_pid); 225 EXPECT_EQ(kCrashSignal, status->pr_info.si_signo); 226 } 227 ++num_nt_prstatus; 228 if (status->pr_fpvalid) 229 ++num_pr_fpvalid; 230 break; 231 } 232 #if defined(__i386__) || defined(__x86_64__) 233 case NT_FPREGSET: { 234 EXPECT_TRUE(description.data() != NULL); 235 EXPECT_EQ(sizeof(user_fpregs_struct), description.length()); 236 ++num_nt_fpregset; 237 break; 238 } 239 #endif 240 #if defined(__i386__) 241 case NT_PRXFPREG: { 242 EXPECT_TRUE(description.data() != NULL); 243 EXPECT_EQ(sizeof(user_fpxregs_struct), description.length()); 244 ++num_nt_prxfpreg; 245 break; 246 } 247 #endif 248 default: 249 break; 250 } 251 note = note.GetNextNote(); 252 } 253 254 #if defined(THREAD_SANITIZER) 255 for (std::set<pid_t>::const_iterator expected = expected_thread_ids.begin(); 256 expected != expected_thread_ids.end(); 257 ++expected) { 258 EXPECT_NE(actual_thread_ids.find(*expected), actual_thread_ids.end()); 259 } 260 EXPECT_GE(num_nt_prstatus, kNumOfThreads); 261 #else 262 EXPECT_EQ(actual_thread_ids, expected_thread_ids); 263 EXPECT_EQ(num_nt_prstatus, kNumOfThreads); 264 #endif 265 EXPECT_EQ(1U, num_nt_prpsinfo); 266 #if defined(__i386__) || defined(__x86_64__) 267 EXPECT_EQ(num_pr_fpvalid, num_nt_fpregset); 268 #endif 269 #if defined(__i386__) 270 EXPECT_EQ(num_pr_fpvalid, num_nt_prxfpreg); 271 #endif 272 }