1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-08-11 08:34:34 +02:00

Update breakpad to make it work with MinGW

This commit is contained in:
Dominik Schmidt
2014-04-14 19:23:44 +02:00
parent c912b76c49
commit b4f05b0831
1049 changed files with 57520 additions and 329083 deletions

View File

@@ -0,0 +1,224 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// A minimalistic implementation of getcontext() to be used by
// Google Breakpad on Android.
#include "common/android/ucontext_constants.h"
/* int getcontext (ucontext_t *ucp) */
#ifdef __arm__
.text
.global breakpad_getcontext
.hidden breakpad_getcontext
.type breakpad_getcontext, #function
.align 0
.fnstart
breakpad_getcontext:
/* First, save r4-r11 */
add r1, r0, #(MCONTEXT_GREGS_OFFSET + 4*4)
stm r1, {r4-r11}
/* r12 is a scratch register, don't save it */
/* Save sp and lr explicitely. */
/* - sp can't be stored with stmia in Thumb-2 */
/* - STM instructions that store sp and pc are deprecated in ARM */
str sp, [r0, #(MCONTEXT_GREGS_OFFSET + 13*4)]
str lr, [r0, #(MCONTEXT_GREGS_OFFSET + 14*4)]
/* Save the caller's address in 'pc' */
str lr, [r0, #(MCONTEXT_GREGS_OFFSET + 15*4)]
/* Save ucontext_t* pointer accross next call */
mov r4, r0
/* Call sigprocmask(SIG_BLOCK, NULL, &(ucontext->uc_sigmask)) */
mov r0, #0 /* SIG_BLOCK */
mov r1, #0 /* NULL */
add r2, r4, #UCONTEXT_SIGMASK_OFFSET
bl sigprocmask(PLT)
/* Intentionally do not save the FPU state here. This is because on
* Linux/ARM, one should instead use ptrace(PTRACE_GETFPREGS) or
* ptrace(PTRACE_GETVFPREGS) to get it.
*
* Note that a real implementation of getcontext() would need to save
* this here to allow setcontext()/swapcontext() to work correctly.
*/
/* Restore the values of r4 and lr */
mov r0, r4
ldr lr, [r0, #(MCONTEXT_GREGS_OFFSET + 14*4)]
ldr r4, [r0, #(MCONTEXT_GREGS_OFFSET + 4*4)]
/* Return 0 */
mov r0, #0
bx lr
.fnend
.size breakpad_getcontext, . - breakpad_getcontext
#elif defined(__i386__)
.text
.global breakpad_getcontext
.hidden breakpad_getcontext
.align 4
.type breakpad_getcontext, @function
breakpad_getcontext:
movl 4(%esp), %eax /* eax = uc */
/* Save register values */
movl %ecx, MCONTEXT_ECX_OFFSET(%eax)
movl %edx, MCONTEXT_EDX_OFFSET(%eax)
movl %ebx, MCONTEXT_EBX_OFFSET(%eax)
movl %edi, MCONTEXT_EDI_OFFSET(%eax)
movl %esi, MCONTEXT_ESI_OFFSET(%eax)
movl %ebp, MCONTEXT_EBP_OFFSET(%eax)
movl (%esp), %edx /* return address */
lea 4(%esp), %ecx /* exclude return address from stack */
mov %edx, MCONTEXT_EIP_OFFSET(%eax)
mov %ecx, MCONTEXT_ESP_OFFSET(%eax)
xorl %ecx, %ecx
movw %fs, %cx
mov %ecx, MCONTEXT_FS_OFFSET(%eax)
movl $0, MCONTEXT_EAX_OFFSET(%eax)
/* Save floating point state to fpregstate, then update
* the fpregs pointer to point to it */
leal UCONTEXT_FPREGS_MEM_OFFSET(%eax), %ecx
fnstenv (%ecx)
fldenv (%ecx)
mov %ecx, UCONTEXT_FPREGS_OFFSET(%eax)
/* Save signal mask: sigprocmask(SIGBLOCK, NULL, &uc->uc_sigmask) */
leal UCONTEXT_SIGMASK_OFFSET(%eax), %edx
xorl %ecx, %ecx
push %edx /* &uc->uc_sigmask */
push %ecx /* NULL */
push %ecx /* SIGBLOCK == 0 on i386 */
call sigprocmask@PLT
addl $12, %esp
movl $0, %eax
ret
.size breakpad_getcontext, . - breakpad_getcontext
#elif defined(__mips__)
#if _MIPS_SIM != _ABIO32
#error "Unsupported mips ISA. Only mips o32 is supported."
#endif
// This implementation is inspired by implementation of getcontext in glibc.
#include <asm/asm.h>
#include <asm/regdef.h>
#include <asm/fpregdef.h>
#include <asm/unistd.h> // for __NR_rt_sigprocmask
#define _NSIG8 128 / 8
#define SIG_BLOCK 1
.text
LOCALS_NUM = 2 // save gp and ra on stack
FRAME_SIZE = ((LOCALS_NUM * SZREG) + ALSZ) & ALMASK
RA_FRAME_OFFSET = FRAME_SIZE - (1 * SZREG)
GP_FRAME_OFFSET = FRAME_SIZE - (2 * SZREG)
MCONTEXT_REG_SIZE = 8
NESTED (breakpad_getcontext, FRAME_SIZE, ra)
.mask 0x00000000, 0
.fmask 0x00000000, 0
.set noreorder
.cpload t9
.set reorder
move a2, sp
#define _SP a2
addiu sp, -FRAME_SIZE
sw ra, RA_FRAME_OFFSET(sp)
sw gp, GP_FRAME_OFFSET(sp)
sw s0, (16 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw s1, (17 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw s2, (18 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw s3, (19 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw s4, (20 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw s5, (21 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw s6, (22 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw s7, (23 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw _SP, (29 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw fp, (30 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw ra, (31 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw ra, MCONTEXT_PC_OFFSET(a0)
#ifdef __mips_hard_float
s.d fs0, (20 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d fs1, (22 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d fs2, (24 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d fs3, (26 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d fs4, (28 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d fs5, (30 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
cfc1 v1, fcr31
sw v1, MCONTEXT_FPC_CSR(a0)
#endif // __mips_hard_float
/* rt_sigprocmask (SIG_BLOCK, NULL, &ucp->uc_sigmask, _NSIG8) */
li a3, _NSIG8
addu a2, a0, UCONTEXT_SIGMASK_OFFSET
move a1, zero
li a0, SIG_BLOCK
li v0, __NR_rt_sigprocmask
syscall
lw ra, RA_FRAME_OFFSET(sp)
lw gp, GP_FRAME_OFFSET(sp)
addiu sp, FRAME_SIZE
jr ra
END (breakpad_getcontext)
#else
#error "This file has not been ported for your CPU!"
#endif

View File

@@ -0,0 +1,94 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sys/ucontext.h>
#include "breakpad_googletest_includes.h"
#include "common/android/ucontext_constants.h"
TEST(AndroidUContext, GRegsOffset) {
#ifdef __arm__
// There is no gregs[] array on ARM, so compare to the offset of
// first register fields, since they're stored in order.
ASSERT_EQ(static_cast<size_t>(MCONTEXT_GREGS_OFFSET),
offsetof(ucontext_t,uc_mcontext.arm_r0));
#elif defined(__i386__)
ASSERT_EQ(static_cast<size_t>(MCONTEXT_GREGS_OFFSET),
offsetof(ucontext_t,uc_mcontext.gregs));
#define CHECK_REG(x) \
ASSERT_EQ(static_cast<size_t>(MCONTEXT_##x##_OFFSET), \
offsetof(ucontext_t,uc_mcontext.gregs[REG_##x]))
CHECK_REG(GS);
CHECK_REG(FS);
CHECK_REG(ES);
CHECK_REG(DS);
CHECK_REG(EDI);
CHECK_REG(ESI);
CHECK_REG(EBP);
CHECK_REG(ESP);
CHECK_REG(EBX);
CHECK_REG(EDX);
CHECK_REG(ECX);
CHECK_REG(EAX);
CHECK_REG(TRAPNO);
CHECK_REG(ERR);
CHECK_REG(EIP);
CHECK_REG(CS);
CHECK_REG(EFL);
CHECK_REG(UESP);
CHECK_REG(SS);
ASSERT_EQ(static_cast<size_t>(UCONTEXT_FPREGS_OFFSET),
offsetof(ucontext_t,uc_mcontext.fpregs));
ASSERT_EQ(static_cast<size_t>(UCONTEXT_FPREGS_MEM_OFFSET),
offsetof(ucontext_t,__fpregs_mem));
#elif defined(__mips__)
ASSERT_EQ(static_cast<size_t>(MCONTEXT_GREGS_OFFSET),
offsetof(ucontext_t,uc_mcontext.gregs));
// PC for mips is not part of gregs.
ASSERT_EQ(static_cast<size_t>(MCONTEXT_PC_OFFSET),
offsetof(ucontext_t,uc_mcontext.pc));
ASSERT_EQ(static_cast<size_t>(MCONTEXT_FPREGS_OFFSET),
offsetof(ucontext_t,uc_mcontext.fpregs));
ASSERT_EQ(static_cast<size_t>(MCONTEXT_FPC_CSR),
offsetof(ucontext_t,uc_mcontext.fpc_csr));
#else
ASSERT_EQ(static_cast<size_t>(MCONTEXT_GREGS_OFFSET),
offsetof(ucontext_t,uc_mcontext.gregs));
#endif
}
TEST(AndroidUContext, SigmakOffset) {
ASSERT_EQ(static_cast<size_t>(UCONTEXT_SIGMASK_OFFSET),
offsetof(ucontext_t,uc_sigmask));
}

View File

@@ -0,0 +1,163 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ELF_H
#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ELF_H
#include <stdint.h>
#include <libgen.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// The Android <elf.h> provides BSD-based definitions for the ElfXX_Nhdr
// types
// always source-compatible with the GLibc/kernel ones. To overcome this
// issue without modifying a lot of code in Breakpad, use an ugly macro
// renaming trick with #include_next
// Avoid conflict with BSD-based definition of ElfXX_Nhdr.
// Unfortunately, their field member names do not use a 'n_' prefix.
#define Elf32_Nhdr __bsd_Elf32_Nhdr
#define Elf64_Nhdr __bsd_Elf64_Nhdr
// In case they are defined by the NDK version
#define Elf32_auxv_t __bionic_Elf32_auxv_t
#define Elf64_auxv_t __bionic_Elf64_auxv_t
#define Elf32_Dyn __bionic_Elf32_Dyn
#define Elf64_Dyn __bionic_Elf64_Dyn
#include_next <elf.h>
#undef Elf32_Nhdr
#undef Elf64_Nhdr
typedef struct {
Elf32_Word n_namesz;
Elf32_Word n_descsz;
Elf32_Word n_type;
} Elf32_Nhdr;
typedef struct {
Elf64_Word n_namesz;
Elf64_Word n_descsz;
Elf64_Word n_type;
} Elf64_Nhdr;
#undef Elf32_auxv_t
#undef Elf64_auxv_t
typedef struct {
uint32_t a_type;
union {
uint32_t a_val;
} a_un;
} Elf32_auxv_t;
typedef struct {
uint64_t a_type;
union {
uint64_t a_val;
} a_un;
} Elf64_auxv_t;
#undef Elf32_Dyn
#undef Elf64_Dyn
typedef struct {
Elf32_Sword d_tag;
union {
Elf32_Word d_val;
Elf32_Addr d_ptr;
} d_un;
} Elf32_Dyn;
typedef struct {
Elf64_Sxword d_tag;
union {
Elf64_Xword d_val;
Elf64_Addr d_ptr;
} d_un;
} Elf64_Dyn;
// __WORDSIZE is GLibc-specific and used by Google Breakpad on Linux.
// All Android platforms are 32-bit for now.
#ifndef __WORDSIZE
#define __WORDSIZE 32
#endif
// The Android headers don't always define this constant.
#ifndef EM_X86_64
#define EM_X86_64 62
#endif
#ifndef EM_PPC64
#define EM_PPC64 21
#endif
#ifndef EM_S390
#define EM_S390 22
#endif
#if !defined(AT_SYSINFO_EHDR)
#define AT_SYSINFO_EHDR 33
#endif
#if !defined(NT_PRSTATUS)
#define NT_PRSTATUS 1
#endif
#if !defined(NT_PRPSINFO)
#define NT_PRPSINFO 3
#endif
#if !defined(NT_AUXV)
#define NT_AUXV 6
#endif
#if !defined(NT_PRXFPREG)
#define NT_PRXFPREG 0x46e62b7f
#endif
#if !defined(NT_FPREGSET)
#define NT_FPREGSET 2
#endif
#if !defined(SHT_MIPS_DWARF)
#define SHT_MIPS_DWARF 0x7000001e
#endif
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ELF_H

View File

@@ -0,0 +1,67 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_BREAKPAD_ANDROID_INCLUDE_LINK_H
#define GOOGLE_BREAKPAD_ANDROID_INCLUDE_LINK_H
/* Android doesn't provide <link.h>. Provide custom version here */
#include <elf.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#define ElfW(type) _ElfW (Elf, ELFSIZE, type)
#define _ElfW(e,w,t) _ElfW_1 (e, w, _##t)
#define _ElfW_1(e,w,t) e##w##t
struct r_debug {
int r_version;
struct link_map* r_map;
ElfW(Addr) r_brk;
enum {
RT_CONSISTENT,
RT_ADD,
RT_DELETE } r_state;
ElfW(Addr) r_ldbase;
};
struct link_map {
ElfW(Addr) l_addr;
char* l_name;
ElfW(Dyn)* l_ld;
struct link_map* l_next;
struct link_map* l_prev;
};
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif /* GOOGLE_BREAKPAD_ANDROID_INCLUDE_LINK_H */

View File

@@ -0,0 +1,51 @@
// Copyright (c) 2013, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_BREAKPAD_ANDROID_INCLUDE_SGIDEFS_H
#define GOOGLE_BREAKPAD_ANDROID_INCLUDE_SGIDEFS_H
#ifdef __mips__
#define _MIPS_SIM_ABI32 1
#define _MIPS_SIM_NABI32 2
#define _MIPS_SIM_ABI64 3
// The following should always be defined by the compiler,
// Verified for Android with GCC 4.6 and Clang 3.1.
#ifndef _MIPS_SIM
#error "Toolchain should define _MIPS_SIM"
#endif
#ifndef _MIPS_SZPTR
#error "Toolchain should define _MIPS_PTR"
#endif
#endif // __mips__
#endif // GOOGLE_BREAKPAD_ANDROID_INCLUDE_SGIDEFS_H

View File

@@ -0,0 +1,100 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_STAB_H
#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_STAB_H
#include <sys/cdefs.h>
#ifdef __BIONIC_HAVE_STAB_H
#include <stab.h>
#else
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#define _STAB_CODE_LIST \
_STAB_CODE_DEF(UNDF,0x00) \
_STAB_CODE_DEF(GSYM,0x20) \
_STAB_CODE_DEF(FNAME,0x22) \
_STAB_CODE_DEF(FUN,0x24) \
_STAB_CODE_DEF(STSYM,0x26) \
_STAB_CODE_DEF(LCSYM,0x28) \
_STAB_CODE_DEF(MAIN,0x2a) \
_STAB_CODE_DEF(PC,0x30) \
_STAB_CODE_DEF(NSYMS,0x32) \
_STAB_CODE_DEF(NOMAP,0x34) \
_STAB_CODE_DEF(OBJ,0x38) \
_STAB_CODE_DEF(OPT,0x3c) \
_STAB_CODE_DEF(RSYM,0x40) \
_STAB_CODE_DEF(M2C,0x42) \
_STAB_CODE_DEF(SLINE,0x44) \
_STAB_CODE_DEF(DSLINE,0x46) \
_STAB_CODE_DEF(BSLINE,0x48) \
_STAB_CODE_DEF(BROWS,0x48) \
_STAB_CODE_DEF(DEFD,0x4a) \
_STAB_CODE_DEF(EHDECL,0x50) \
_STAB_CODE_DEF(MOD2,0x50) \
_STAB_CODE_DEF(CATCH,0x54) \
_STAB_CODE_DEF(SSYM,0x60) \
_STAB_CODE_DEF(SO,0x64) \
_STAB_CODE_DEF(LSYM,0x80) \
_STAB_CODE_DEF(BINCL,0x82) \
_STAB_CODE_DEF(SOL,0x84) \
_STAB_CODE_DEF(PSYM,0xa0) \
_STAB_CODE_DEF(EINCL,0xa2) \
_STAB_CODE_DEF(ENTRY,0xa4) \
_STAB_CODE_DEF(LBRAC,0xc0) \
_STAB_CODE_DEF(EXCL,0xc2) \
_STAB_CODE_DEF(SCOPE,0xc4) \
_STAB_CODE_DEF(RBRAC,0xe0) \
_STAB_CODE_DEF(BCOMM,0xe2) \
_STAB_CODE_DEF(ECOMM,0xe4) \
_STAB_CODE_DEF(ECOML,0xe8) \
_STAB_CODE_DEF(NBTEXT,0xf0) \
_STAB_CODE_DEF(NBDATA,0xf2) \
_STAB_CODE_DEF(NBBSS,0xf4) \
_STAB_CODE_DEF(NBSTS,0xf6) \
_STAB_CODE_DEF(NBLCS,0xf8) \
_STAB_CODE_DEF(LENG,0xfe)
enum __stab_debug_code {
#define _STAB_CODE_DEF(x,y) N_##x = y,
_STAB_CODE_LIST
#undef _STAB_CODE_DEF
};
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // __BIONIC_HAVE_STAB_H
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_STAB_H

View File

@@ -0,0 +1,113 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_SYS_PROCFS_H
#define GOOGLE_BREAKPAD_COMMON_ANDROID_SYS_PROCFS_H
#ifdef __BIONIC_HAVE_SYS_PROCFS_H
#include_next <sys/procfs.h>
#else
#include <sys/cdefs.h>
#include <sys/user.h>
#include <unistd.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#ifdef __x86_64__
typedef unsigned long long elf_greg_t;
#else
typedef unsigned long elf_greg_t;
#endif
#ifdef __arm__
#define ELF_NGREG (sizeof(struct user_regs) / sizeof(elf_greg_t))
#else
#define ELF_NGREG (sizeof(struct user_regs_struct) / sizeof(elf_greg_t))
#endif
typedef elf_greg_t elf_gregset_t[ELF_NGREG];
struct elf_siginfo {
int si_signo;
int si_code;
int si_errno;
};
struct elf_prstatus {
struct elf_siginfo pr_info;
short pr_cursig;
unsigned long pr_sigpend;
unsigned long pr_sighold;
pid_t pr_pid;
pid_t pr_ppid;
pid_t pr_pgrp;
pid_t pd_sid;
struct timeval pr_utime;
struct timeval pr_stime;
struct timeval pr_cutime;
struct timeval pr_cstime;
elf_gregset_t pr_reg;
int pr_fpvalid;
};
#define ELF_PRARGSZ 80
struct elf_prpsinfo {
char pr_state;
char pr_sname;
char pr_zomb;
char pr_nice;
unsigned long pr_flags;
#ifdef __x86_64__
unsigned int pr_uid;
unsigned int pr_gid;
#else
unsigned short pr_uid;
unsigned short pr_gid;
#endif
int pr_pid;
int pr_ppid;
int pr_pgrp;
int pr_sid;
char pr_fname[16];
char pr_psargs[ELF_PRARGSZ];
};
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // __BIONIC_HAVE_SYS_PROCFS_H
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_SYS_PROCFS_H

View File

@@ -0,0 +1,35 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_SIGNAL_H
#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_SIGNAL_H
#include <signal.h>
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_SIGNAL_H

View File

@@ -0,0 +1,39 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_STAT_H
#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_STAT_H
#include_next <sys/stat.h>
#ifndef S_IRWXU
#define S_IRWXU 00700
#endif
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_STAT_H

View File

@@ -0,0 +1,174 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_UCONTEXT_H
#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_UCONTEXT_H
#include <sys/cdefs.h>
#include <signal.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#ifndef __BIONIC_HAVE_UCONTEXT_T
// Ensure that 'stack_t' is defined.
#include <asm/signal.h>
// This version of the Android C library headers do not provide ucontext_t.
// Provide custom definitions for Google Breakpad.
#if defined(__arm__)
// Ensure that 'struct sigcontext' is defined.
#include <asm/sigcontext.h>
typedef struct sigcontext mcontext_t;
// The ARM kernel uses a 64-bit signal mask.
typedef uint32_t kernel_sigmask_t[2];
typedef struct ucontext {
uint32_t uc_flags;
struct ucontext* uc_link;
stack_t uc_stack;
mcontext_t uc_mcontext;
kernel_sigmask_t uc_sigmask;
// Other fields are not used by Google Breakpad. Don't define them.
} ucontext_t;
#elif defined(__i386__)
/* 80-bit floating-point register */
struct _libc_fpreg {
unsigned short significand[4];
unsigned short exponent;
};
/* Simple floating-point state, see FNSTENV instruction */
struct _libc_fpstate {
unsigned long cw;
unsigned long sw;
unsigned long tag;
unsigned long ipoff;
unsigned long cssel;
unsigned long dataoff;
unsigned long datasel;
struct _libc_fpreg _st[8];
unsigned long status;
};
typedef uint32_t greg_t;
typedef struct {
uint32_t gregs[19];
struct _libc_fpstate* fpregs;
uint32_t oldmask;
uint32_t cr2;
} mcontext_t;
enum {
REG_GS = 0,
REG_FS,
REG_ES,
REG_DS,
REG_EDI,
REG_ESI,
REG_EBP,
REG_ESP,
REG_EBX,
REG_EDX,
REG_ECX,
REG_EAX,
REG_TRAPNO,
REG_ERR,
REG_EIP,
REG_CS,
REG_EFL,
REG_UESP,
REG_SS,
};
// The i386 kernel uses a 64-bit signal mask.
typedef uint32_t kernel_sigmask_t[2];
typedef struct ucontext {
uint32_t uc_flags;
struct ucontext* uc_link;
stack_t uc_stack;
mcontext_t uc_mcontext;
kernel_sigmask_t uc_sigmask;
struct _libc_fpstate __fpregs_mem;
} ucontext_t;
#elif defined(__mips__)
typedef struct {
uint32_t regmask;
uint32_t status;
uint64_t pc;
uint64_t gregs[32];
uint64_t fpregs[32];
uint32_t acx;
uint32_t fpc_csr;
uint32_t fpc_eir;
uint32_t used_math;
uint32_t dsp;
uint64_t mdhi;
uint64_t mdlo;
uint32_t hi1;
uint32_t lo1;
uint32_t hi2;
uint32_t lo2;
uint32_t hi3;
uint32_t lo3;
} mcontext_t;
// The MIPS kernel uses a 128-bit signal mask.
typedef uint32_t kernel_sigmask_t[4];
typedef struct ucontext {
uint32_t uc_flags;
struct ucontext* uc_link;
stack_t uc_stack;
mcontext_t uc_mcontext;
kernel_sigmask_t uc_sigmask;
// Other fields are not used by Google Breakpad. Don't define them.
} ucontext_t;
#else
# error "Unsupported Android CPU ABI!"
#endif
#endif // __BIONIC_HAVE_UCONTEXT_T
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_UCONTEXT_H

View File

@@ -0,0 +1,149 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_USER_H
#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_USER_H
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// These types are used with ptrace(), more specifically with
// PTRACE_GETREGS, PTRACE_GETFPREGS and PTRACE_GETVFPREGS respectively.
//
// They are also defined, sometimes with different names, in <asm/user.h>
//
#if defined(__arm__)
#define _ARM_USER_H 1 // Prevent <asm/user.h> conflicts
// Note: on ARM, GLibc uses user_regs instead of user_regs_struct.
struct user_regs {
// Note: Entries 0-15 match r0..r15
// Entry 16 is used to store the CPSR register.
// Entry 17 is used to store the "orig_r0" value.
unsigned long int uregs[18];
};
// Same here: user_fpregs instead of user_fpregs_struct.
struct user_fpregs {
struct fp_reg {
unsigned int sign1:1;
unsigned int unused:15;
unsigned int sign2:1;
unsigned int exponent:14;
unsigned int j:1;
unsigned int mantissa1:31;
unsigned int mantissa0:32;
} fpregs[8];
unsigned int fpsr:32;
unsigned int fpcr:32;
unsigned char ftype[8];
unsigned int init_flag;
};
// GLibc doesn't define this one in <sys/user.h> though.
struct user_vfpregs {
unsigned long long fpregs[32];
unsigned long fpscr;
};
#elif defined(__i386__)
#define _I386_USER_H 1 // Prevent <asm/user.h> conflicts
// GLibc-compatible definitions
struct user_regs_struct {
long ebx, ecx, edx, esi, edi, ebp, eax;
long xds, xes, xfs, xgs, orig_eax;
long eip, xcs, eflags, esp, xss;
};
struct user_fpregs_struct {
long cwd, swd, twd, fip, fcs, foo, fos;
long st_space[20];
};
struct user_fpxregs_struct {
unsigned short cwd, swd, twd, fop;
long fip, fcs, foo, fos, mxcsr, reserved;
long st_space[32];
long xmm_space[32];
long padding[56];
};
struct user {
struct user_regs_struct regs;
int u_fpvalid;
struct user_fpregs_struct i387;
unsigned long u_tsize;
unsigned long u_dsize;
unsigned long u_ssize;
unsigned long start_code;
unsigned long start_stack;
long signal;
int reserved;
struct user_regs_struct* u_ar0;
struct user_fpregs_struct* u_fpstate;
unsigned long magic;
char u_comm [32];
int u_debugreg [8];
};
#elif defined(__mips__)
#define _ASM_USER_H 1 // Prevent <asm/user.h> conflicts
struct user_regs_struct {
unsigned long long regs[32];
unsigned long long lo;
unsigned long long hi;
unsigned long long epc;
unsigned long long badvaddr;
unsigned long long status;
unsigned long long cause;
};
struct user_fpregs_struct {
unsigned long long regs[32];
unsigned int fpcsr;
unsigned int fir;
};
#else
# error "Unsupported Android CPU ABI"
#endif
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_USER_H

View File

@@ -0,0 +1,56 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_UCONTEXT_H
#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_UCONTEXT_H
#include <sys/cdefs.h>
#ifdef __BIONIC_UCONTEXT_H
#include <ucontext.h>
#else
#include <sys/ucontext.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Provided by src/android/common/breakpad_getcontext.S
int breakpad_getcontext(ucontext_t* ucp);
#define getcontext(x) breakpad_getcontext(x)
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // __BIONIC_UCONTEXT_H
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_UCONTEXT_H

View File

@@ -0,0 +1,72 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Android doesn't provide wcscasecmp(), so provide an alternative here.
//
// Note that this header is not needed when Breakpad is compiled against
// a recent version of Googletest. It shall be considered for removal once
// src/testing/ is updated to an appropriate revision in the future.
#ifndef GOOGLEBREAKPAD_COMMON_ANDROID_INCLUDE_WCHAR_H
#define GOOGLEBREAKPAD_COMMON_ANDROID_INCLUDE_WCHAR_H
#include_next <wchar.h>
// This needs to be in an extern "C" namespace, or Googletest will not
// compile against it.
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
static wchar_t inline wcstolower(wchar_t ch) {
if (ch >= L'a' && ch <= L'A')
ch -= L'a' - L'A';
return ch;
}
static int inline wcscasecmp(const wchar_t* s1, const wchar_t* s2) {
for (;;) {
wchar_t c1 = wcstolower(*s1);
wchar_t c2 = wcstolower(*s2);
if (c1 < c2)
return -1;
if (c1 > c2)
return 1;
if (c1 == L'0')
return 0;
s1++;
s2++;
}
}
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // GOOGLEBREAKPAD_COMMON_ANDROID_INCLUDE_WCHAR_H

View File

@@ -0,0 +1,110 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// mkdtemp() wasn't declared in <stdlib.h> until NDK r9b due to a simple
// packaging bug (the function has always been implemented in all versions
// of the C library). This header is provided to build Breakpad with earlier
// NDK revisions (e.g. the one used by Chromium). It may be removed in the
// future once all major projects upgrade to use a more recent NDK.
//
// The reason this is inlined here is to avoid linking a new object file
// into each unit test program (i.e. keep build files simple).
#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_TESTING_MKDTEMP_H
#define GOOGLE_BREAKPAD_COMMON_ANDROID_TESTING_MKDTEMP_H
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
// Using a macro renaming trick here is necessary when building against
// NDK r9b. Otherwise the compiler will complain that calls to mkdtemp()
// are ambiguous.
#define mkdtemp breakpad_mkdtemp
namespace {
char* breakpad_mkdtemp(char* path) {
if (path == NULL) {
errno = EINVAL;
return NULL;
}
// 'path' must be terminated with six 'X'
const char kSuffix[] = "XXXXXX";
const size_t kSuffixLen = strlen(kSuffix);
char* path_end = path + strlen(path);
if (static_cast<size_t>(path_end - path) < kSuffixLen ||
memcmp(path_end - kSuffixLen, kSuffix, kSuffixLen) != 0) {
errno = EINVAL;
return NULL;
}
// If 'path' contains a directory separator, check that it exists to
// avoid looping later.
char* sep = strrchr(path, '/');
if (sep != NULL) {
struct stat st;
int ret;
*sep = '\0'; // temporarily zero-terminate the dirname.
ret = stat(path, &st);
*sep = '/'; // restore full path.
if (ret < 0)
return NULL;
if (!S_ISDIR(st.st_mode)) {
errno = ENOTDIR;
return NULL;
}
}
// Loop. On each iteration, replace the XXXXXX suffix with a random
// number.
int tries;
for (tries = 128; tries > 0; tries--) {
int random = rand() % 1000000;
snprintf(path_end - kSuffixLen, kSuffixLen + 1, "%0d", random);
if (mkdir(path, 0700) == 0)
return path; // Success
if (errno != EEXIST)
return NULL;
}
assert(errno == EEXIST);
return NULL;
}
} // namespace
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_TESTING_MKDTEMP_H

View File

@@ -0,0 +1,99 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This contains Pthread-related functions not provided by the Android NDK
// but required by the Breakpad unit test. The functions are inlined here
// in a C++ anonymous namespace in order to keep the build files simples.
#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_TESTING_PTHREAD_FIXES_H
#define GOOGLE_BREAKPAD_COMMON_ANDROID_TESTING_PTHREAD_FIXES_H
#include <pthread.h>
namespace {
// Android doesn't provide pthread_barrier_t for now.
#ifndef PTHREAD_BARRIER_SERIAL_THREAD
// Anything except 0 will do here.
#define PTHREAD_BARRIER_SERIAL_THREAD 0x12345
typedef struct {
pthread_mutex_t mutex;
pthread_cond_t cond;
unsigned count;
} pthread_barrier_t;
int pthread_barrier_init(pthread_barrier_t* barrier,
const void* /* barrier_attr */,
unsigned count) {
barrier->count = count;
pthread_mutex_init(&barrier->mutex, NULL);
pthread_cond_init(&barrier->cond, NULL);
return 0;
}
int pthread_barrier_wait(pthread_barrier_t* barrier) {
// Lock the mutex
pthread_mutex_lock(&barrier->mutex);
// Decrement the count. If this is the first thread to reach 0, wake up
// waiters, unlock the mutex, then return PTHREAD_BARRIER_SERIAL_THREAD.
if (--barrier->count == 0) {
// First thread to reach the barrier
pthread_cond_broadcast(&barrier->cond);
pthread_mutex_unlock(&barrier->mutex);
return PTHREAD_BARRIER_SERIAL_THREAD;
}
// Otherwise, wait for other threads until the count reaches 0, then
// return 0 to indicate this is not the first thread.
do {
pthread_cond_wait(&barrier->cond, &barrier->mutex);
} while (barrier->count > 0);
pthread_mutex_unlock(&barrier->mutex);
return 0;
}
int pthread_barrier_destroy(pthread_barrier_t *barrier) {
barrier->count = 0;
pthread_cond_destroy(&barrier->cond);
pthread_mutex_destroy(&barrier->mutex);
return 0;
}
#endif // defined(PTHREAD_BARRIER_SERIAL_THREAD)
int pthread_yield(void) {
sched_yield();
return 0;
}
} // namespace
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_TESTING_PTHREAD_FIXES_H

View File

@@ -0,0 +1,88 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This header can be included either from a C, C++ or Assembly file.
// Its purpose is to contain constants that must match the offsets of
// various fields in ucontext_t.
//
// They should match the definitions from
// src/common/android/include/sys/ucontext.h
//
// Used by src/common/android/breakpad_getcontext.S
// Tested by src/common/android/testing/breakpad_getcontext_unittest.cc
#ifndef GOOGLEBREAKPAD_COMMON_ANDROID_UCONTEXT_CONSTANTS_H
#define GOOGLEBREAKPAD_COMMON_ANDROID_UCONTEXT_CONSTANTS_H
#if defined(__arm__)
#define MCONTEXT_GREGS_OFFSET 32
#define UCONTEXT_SIGMASK_OFFSET 104
#elif defined(__i386__)
#define MCONTEXT_GREGS_OFFSET 20
#define MCONTEXT_GS_OFFSET (MCONTEXT_GREGS_OFFSET + 0*4)
#define MCONTEXT_FS_OFFSET (MCONTEXT_GREGS_OFFSET + 1*4)
#define MCONTEXT_ES_OFFSET (MCONTEXT_GREGS_OFFSET + 2*4)
#define MCONTEXT_DS_OFFSET (MCONTEXT_GREGS_OFFSET + 3*4)
#define MCONTEXT_EDI_OFFSET (MCONTEXT_GREGS_OFFSET + 4*4)
#define MCONTEXT_ESI_OFFSET (MCONTEXT_GREGS_OFFSET + 5*4)
#define MCONTEXT_EBP_OFFSET (MCONTEXT_GREGS_OFFSET + 6*4)
#define MCONTEXT_ESP_OFFSET (MCONTEXT_GREGS_OFFSET + 7*4)
#define MCONTEXT_EBX_OFFSET (MCONTEXT_GREGS_OFFSET + 8*4)
#define MCONTEXT_EDX_OFFSET (MCONTEXT_GREGS_OFFSET + 9*4)
#define MCONTEXT_ECX_OFFSET (MCONTEXT_GREGS_OFFSET + 10*4)
#define MCONTEXT_EAX_OFFSET (MCONTEXT_GREGS_OFFSET + 11*4)
#define MCONTEXT_TRAPNO_OFFSET (MCONTEXT_GREGS_OFFSET + 12*4)
#define MCONTEXT_ERR_OFFSET (MCONTEXT_GREGS_OFFSET + 13*4)
#define MCONTEXT_EIP_OFFSET (MCONTEXT_GREGS_OFFSET + 14*4)
#define MCONTEXT_CS_OFFSET (MCONTEXT_GREGS_OFFSET + 15*4)
#define MCONTEXT_EFL_OFFSET (MCONTEXT_GREGS_OFFSET + 16*4)
#define MCONTEXT_UESP_OFFSET (MCONTEXT_GREGS_OFFSET + 17*4)
#define MCONTEXT_SS_OFFSET (MCONTEXT_GREGS_OFFSET + 18*4)
#define UCONTEXT_SIGMASK_OFFSET 108
#define UCONTEXT_FPREGS_OFFSET 96
#define UCONTEXT_FPREGS_MEM_OFFSET 116
#elif defined(__mips__)
#define MCONTEXT_PC_OFFSET 32
#define MCONTEXT_GREGS_OFFSET 40
#define MCONTEXT_FPREGS_OFFSET 296
#define MCONTEXT_FPC_CSR 556
#define UCONTEXT_SIGMASK_OFFSET 616
#else
#error "This header has not been ported for your CPU"
#endif
#endif // GOOGLEBREAKPAD_COMMON_ANDROID_UCONTEXT_CONSTANTS_H

View File

@@ -32,8 +32,27 @@
// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#ifndef DISALLOW_COPY_AND_ASSIGN
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)
#endif // DISALLOW_COPY_AND_ASSIGN
namespace google_breakpad {
// Used to explicitly mark the return value of a function as unused. If you are
// really sure you don't want to do anything with the return value of a function
// that has been marked with __attribute__((warn_unused_result)), wrap it with
// this. Example:
//
// scoped_ptr<MyType> my_var = ...;
// if (TakeOwnership(my_var.get()) == SUCCESS)
// ignore_result(my_var.release());
//
template<typename T>
inline void ignore_result(const T&) {
}
} // namespace google_breakpad
#endif // COMMON_BASICTYPES_H_

View File

@@ -45,6 +45,8 @@
#include <string.h>
#include <string>
#include "common/using_std_string.h"
namespace google_breakpad {
// A buffer holding a series of bytes.
@@ -164,7 +166,7 @@ class ByteCursor {
// byte buffer does not contain a terminating zero, clear this cursor's
// complete_ flag, and set STR to the empty string. Return a reference to
// this cursor.
ByteCursor &CString(std::string *str) {
ByteCursor &CString(string *str) {
const uint8_t *end
= static_cast<const uint8_t *>(memchr(here_, '\0', Available()));
if (end) {
@@ -191,7 +193,7 @@ class ByteCursor {
//
// - Otherwise, set *STR to a copy of those LIMIT bytes, and advance the
// cursor by LIMIT bytes.
ByteCursor &CString(std::string *str, size_t limit) {
ByteCursor &CString(string *str, size_t limit) {
if (CheckAvailable(limit)) {
const uint8_t *end
= static_cast<const uint8_t *>(memchr(here_, '\0', limit));

View File

@@ -36,12 +36,12 @@
#include <string.h>
#include "common/byte_cursor.h"
#include "breakpad_googletest_includes.h"
#include "common/byte_cursor.h"
#include "common/using_std_string.h"
using google_breakpad::ByteBuffer;
using google_breakpad::ByteCursor;
using std::string;
TEST(Buffer, SizeOfNothing) {
uint8_t data[1];

View File

@@ -53,8 +53,13 @@ static const UTF32 halfMask = 0x3FFUL;
#define UNI_SUR_HIGH_END (UTF32)0xDBFF
#define UNI_SUR_LOW_START (UTF32)0xDC00
#define UNI_SUR_LOW_END (UTF32)0xDFFF
#ifndef false
#define false 0
#endif
#ifndef true
#define true 1
#endif
/* --------------------------------------------------------------------- */

View File

@@ -20,6 +20,9 @@
* remains attached.
*/
#ifndef COMMON_CONVERT_UTF_H_
#define COMMON_CONVERT_UTF_H_
/* ---------------------------------------------------------------------
Conversions between UTF32, UTF-16, and UTF-8. Header file.
@@ -141,3 +144,5 @@ Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd);
#endif
/* --------------------------------------------------------------------- */
#endif // COMMON_CONVERT_UTF_H_

View File

@@ -37,6 +37,7 @@
#include "common/dwarf/bytereader.h"
#include "common/dwarf/bytereader-inl.h"
#include "common/dwarf/cfi_assembler.h"
#include "common/using_std_string.h"
using dwarf2reader::ByteReader;
using dwarf2reader::DwarfPointerEncoding;
@@ -47,7 +48,6 @@ using google_breakpad::test_assembler::Label;
using google_breakpad::test_assembler::kBigEndian;
using google_breakpad::test_assembler::kLittleEndian;
using google_breakpad::test_assembler::Section;
using std::string;
using testing::Test;
struct ReaderFixture {

View File

@@ -41,10 +41,10 @@ namespace google_breakpad {
using dwarf2reader::DwarfPointerEncoding;
CFISection &CFISection::CIEHeader(u_int64_t code_alignment_factor,
CFISection &CFISection::CIEHeader(uint64_t code_alignment_factor,
int data_alignment_factor,
unsigned return_address_register,
u_int8_t version,
uint8_t version,
const string &augmentation,
bool dwarf64) {
assert(!entry_length_);
@@ -73,8 +73,8 @@ CFISection &CFISection::CIEHeader(u_int64_t code_alignment_factor,
}
CFISection &CFISection::FDEHeader(Label cie_pointer,
u_int64_t initial_location,
u_int64_t address_range,
uint64_t initial_location,
uint64_t address_range,
bool dwarf64) {
assert(!entry_length_);
entry_length_ = new PendingLength();
@@ -117,7 +117,7 @@ CFISection &CFISection::FinishEntry() {
return *this;
}
CFISection &CFISection::EncodedPointer(u_int64_t address,
CFISection &CFISection::EncodedPointer(uint64_t address,
DwarfPointerEncoding encoding,
const EncodedPointerBases &bases) {
// Omitted data is extremely easy to emit.
@@ -131,7 +131,7 @@ CFISection &CFISection::EncodedPointer(u_int64_t address,
// Find the base address to which this pointer is relative. The upper
// nybble of the encoding specifies this.
u_int64_t base;
uint64_t base;
switch (encoding & 0xf0) {
case dwarf2reader::DW_EH_PE_absptr: base = 0; break;
case dwarf2reader::DW_EH_PE_pcrel: base = bases.cfi + Size(); break;
@@ -189,10 +189,10 @@ CFISection &CFISection::EncodedPointer(u_int64_t address,
return *this;
};
const u_int32_t CFISection::kDwarf64InitialLengthMarker;
const u_int32_t CFISection::kDwarf32CIEIdentifier;
const u_int64_t CFISection::kDwarf64CIEIdentifier;
const u_int32_t CFISection::kEHFrame32CIEIdentifier;
const u_int64_t CFISection::kEHFrame64CIEIdentifier;
const uint32_t CFISection::kDwarf64InitialLengthMarker;
const uint32_t CFISection::kDwarf32CIEIdentifier;
const uint64_t CFISection::kDwarf64CIEIdentifier;
const uint32_t CFISection::kEHFrame32CIEIdentifier;
const uint64_t CFISection::kEHFrame64CIEIdentifier;
} // namespace google_breakpad

View File

@@ -41,6 +41,7 @@
#include "common/dwarf/dwarf2enums.h"
#include "common/test_assembler.h"
#include "common/using_std_string.h"
#include "google_breakpad/common/breakpad_types.h"
namespace google_breakpad {
@@ -49,7 +50,6 @@ using dwarf2reader::DwarfPointerEncoding;
using google_breakpad::test_assembler::Endianness;
using google_breakpad::test_assembler::Label;
using google_breakpad::test_assembler::Section;
using std::string;
class CFISection: public Section {
public:
@@ -80,14 +80,14 @@ class CFISection: public Section {
// The starting address of this CFI section in memory, for
// DW_EH_PE_pcrel. DW_EH_PE_pcrel pointers may only be used in data
// that has is loaded into the program's address space.
u_int64_t cfi;
uint64_t cfi;
// The starting address of this file's .text section, for DW_EH_PE_textrel.
u_int64_t text;
uint64_t text;
// The starting address of this file's .got or .eh_frame_hdr section,
// for DW_EH_PE_datarel.
u_int64_t data;
uint64_t data;
};
// Create a CFISection whose endianness is ENDIANNESS, and where
@@ -133,10 +133,10 @@ class CFISection: public Section {
// Before calling this function, you will typically want to use Mark
// or Here to make a label to pass to FDEHeader that refers to this
// CIE's position in the section.
CFISection &CIEHeader(u_int64_t code_alignment_factor,
CFISection &CIEHeader(uint64_t code_alignment_factor,
int data_alignment_factor,
unsigned return_address_register,
u_int8_t version = 3,
uint8_t version = 3,
const string &augmentation = "",
bool dwarf64 = false);
@@ -151,8 +151,8 @@ class CFISection: public Section {
// value.) Nor does it support .debug_frame sections longer than
// 0xffffff00 bytes.
CFISection &FDEHeader(Label cie_pointer,
u_int64_t initial_location,
u_int64_t address_range,
uint64_t initial_location,
uint64_t address_range,
bool dwarf64 = false);
// Note the current position as the end of the last CIE or FDE we
@@ -171,7 +171,7 @@ class CFISection: public Section {
// Append ADDRESS to this section, in the appropriate size and
// endianness. Return a reference to this section.
CFISection &Address(u_int64_t address) {
CFISection &Address(uint64_t address) {
Section::Append(endianness(), address_size_, address);
return *this;
}
@@ -189,26 +189,26 @@ class CFISection: public Section {
//
// (C++ doesn't let me use default arguments here, because I want to
// refer to members of *this in the default argument expression.)
CFISection &EncodedPointer(u_int64_t address) {
CFISection &EncodedPointer(uint64_t address) {
return EncodedPointer(address, pointer_encoding_, encoded_pointer_bases_);
}
CFISection &EncodedPointer(u_int64_t address, DwarfPointerEncoding encoding) {
CFISection &EncodedPointer(uint64_t address, DwarfPointerEncoding encoding) {
return EncodedPointer(address, encoding, encoded_pointer_bases_);
}
CFISection &EncodedPointer(u_int64_t address, DwarfPointerEncoding encoding,
CFISection &EncodedPointer(uint64_t address, DwarfPointerEncoding encoding,
const EncodedPointerBases &bases);
// Restate some member functions, to keep chaining working nicely.
CFISection &Mark(Label *label) { Section::Mark(label); return *this; }
CFISection &D8(u_int8_t v) { Section::D8(v); return *this; }
CFISection &D16(u_int16_t v) { Section::D16(v); return *this; }
CFISection &D8(uint8_t v) { Section::D8(v); return *this; }
CFISection &D16(uint16_t v) { Section::D16(v); return *this; }
CFISection &D16(Label v) { Section::D16(v); return *this; }
CFISection &D32(u_int32_t v) { Section::D32(v); return *this; }
CFISection &D32(uint32_t v) { Section::D32(v); return *this; }
CFISection &D32(const Label &v) { Section::D32(v); return *this; }
CFISection &D64(u_int64_t v) { Section::D64(v); return *this; }
CFISection &D64(uint64_t v) { Section::D64(v); return *this; }
CFISection &D64(const Label &v) { Section::D64(v); return *this; }
CFISection &LEB128(long long v) { Section::LEB128(v); return *this; }
CFISection &ULEB128(u_int64_t v) { Section::ULEB128(v); return *this; }
CFISection &ULEB128(uint64_t v) { Section::ULEB128(v); return *this; }
private:
// A length value that we've appended to the section, but is not yet
@@ -224,13 +224,13 @@ class CFISection: public Section {
// If the first four bytes of an "initial length" are this constant, then
// the data uses the 64-bit DWARF format, and the length itself is the
// subsequent eight bytes.
static const u_int32_t kDwarf64InitialLengthMarker = 0xffffffffU;
static const uint32_t kDwarf64InitialLengthMarker = 0xffffffffU;
// The CIE identifier for 32- and 64-bit DWARF CFI and .eh_frame data.
static const u_int32_t kDwarf32CIEIdentifier = ~(u_int32_t)0;
static const u_int64_t kDwarf64CIEIdentifier = ~(u_int64_t)0;
static const u_int32_t kEHFrame32CIEIdentifier = 0;
static const u_int64_t kEHFrame64CIEIdentifier = 0;
static const uint32_t kDwarf32CIEIdentifier = ~(uint32_t)0;
static const uint64_t kDwarf64CIEIdentifier = ~(uint64_t)0;
static const uint32_t kEHFrame32CIEIdentifier = 0;
static const uint64_t kEHFrame64CIEIdentifier = 0;
// The size of a machine address for the data in this section.
size_t address_size_;
@@ -261,7 +261,7 @@ class CFISection: public Section {
// If in_fde_ is true, this is its starting address. We use this for
// emitting DW_EH_PE_funcrel pointers.
u_int64_t fde_start_address_;
uint64_t fde_start_address_;
};
} // namespace google_breakpad

View File

@@ -31,10 +31,13 @@
// dwarf2diehandler.cc: Implement the dwarf2reader::DieDispatcher class.
// See dwarf2diehandler.h for details.
#include "common/dwarf/dwarf2diehandler.h"
#include <assert.h>
#include <string>
#include "common/dwarf/dwarf2diehandler.h"
#include "common/using_std_string.h"
namespace dwarf2reader {
DIEDispatcher::~DIEDispatcher() {
@@ -54,8 +57,7 @@ bool DIEDispatcher::StartCompilationUnit(uint64 offset, uint8 address_size,
dwarf_version);
}
bool DIEDispatcher::StartDIE(uint64 offset, enum DwarfTag tag,
const AttributeList& attrs) {
bool DIEDispatcher::StartDIE(uint64 offset, enum DwarfTag tag) {
// The stack entry for the parent of this DIE, if there is one.
HandlerStack *parent = die_handlers_.empty() ? NULL : &die_handlers_.top();
@@ -79,7 +81,7 @@ bool DIEDispatcher::StartDIE(uint64 offset, enum DwarfTag tag,
if (parent) {
if (parent->handler_)
// Ask the parent to find a handler.
handler = parent->handler_->FindChildHandler(offset, tag, attrs);
handler = parent->handler_->FindChildHandler(offset, tag);
else
// No parent handler means we're not interested in any of our
// children.
@@ -89,7 +91,7 @@ bool DIEDispatcher::StartDIE(uint64 offset, enum DwarfTag tag,
// decides whether to visit it, but the root DIE has no parent
// handler, so we have a special method on the root DIE handler
// itself to decide.
if (root_handler_->StartRootDIE(offset, tag, attrs))
if (root_handler_->StartRootDIE(offset, tag))
handler = root_handler_;
else
handler = NULL;
@@ -176,7 +178,7 @@ void DIEDispatcher::ProcessAttributeBuffer(uint64 offset,
void DIEDispatcher::ProcessAttributeString(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const std::string& data) {
const string& data) {
HandlerStack &current = die_handlers_.top();
// This had better be an attribute of the DIE we were meant to handle.
assert(offset == current.offset_);

View File

@@ -157,10 +157,12 @@
#define COMMON_DWARF_DWARF2DIEHANDLER_H__
#include <stack>
#include <string>
#include "common/dwarf/types.h"
#include "common/dwarf/dwarf2enums.h"
#include "common/dwarf/dwarf2reader.h"
#include "common/using_std_string.h"
namespace dwarf2reader {
@@ -208,7 +210,7 @@ class DIEHandler {
uint64 len) { }
virtual void ProcessAttributeString(enum DwarfAttribute attr,
enum DwarfForm form,
const std::string& data) { }
const string& data) { }
virtual void ProcessAttributeSignature(enum DwarfAttribute attr,
enum DwarfForm form,
uint64 signture) { }
@@ -237,12 +239,10 @@ class DIEHandler {
// that child DIE (and all its descendants).
//
// OFFSET is the offset of the child; TAG indicates what kind of DIE
// it is; and ATTRS is the list of attributes the DIE will have, and
// their forms (their values are not provided).
// it is.
//
// The default definition skips all children.
virtual DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag,
const AttributeList &attrs) {
virtual DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag) {
return NULL;
}
@@ -278,8 +278,7 @@ class RootDIEHandler: public DIEHandler {
// unit.
//
// The default definition elects to visit the root DIE.
virtual bool StartRootDIE(uint64 offset, enum DwarfTag tag,
const AttributeList& attrs) { return true; }
virtual bool StartRootDIE(uint64 offset, enum DwarfTag tag) { return true; }
};
class DIEDispatcher: public Dwarf2Handler {
@@ -294,8 +293,7 @@ class DIEDispatcher: public Dwarf2Handler {
bool StartCompilationUnit(uint64 offset, uint8 address_size,
uint8 offset_size, uint64 cu_length,
uint8 dwarf_version);
bool StartDIE(uint64 offset, enum DwarfTag tag,
const AttributeList &attrs);
bool StartDIE(uint64 offset, enum DwarfTag tag);
void ProcessAttributeUnsigned(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
@@ -316,7 +314,7 @@ class DIEDispatcher: public Dwarf2Handler {
void ProcessAttributeString(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const std::string &data);
const string &data);
void ProcessAttributeSignature(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,

View File

@@ -38,9 +38,9 @@
#include "breakpad_googletest_includes.h"
#include "common/dwarf/dwarf2diehandler.h"
#include "common/using_std_string.h"
using std::make_pair;
using std::string;
using ::testing::_;
using ::testing::ContainerEq;
@@ -51,7 +51,6 @@ using ::testing::Return;
using ::testing::Sequence;
using ::testing::StrEq;
using dwarf2reader::AttributeList;
using dwarf2reader::DIEDispatcher;
using dwarf2reader::DIEHandler;
using dwarf2reader::DwarfAttribute;
@@ -74,8 +73,7 @@ class MockDIEHandler: public DIEHandler {
MOCK_METHOD3(ProcessAttributeSignature,
void(DwarfAttribute, DwarfForm, uint64));
MOCK_METHOD0(EndAttributes, bool());
MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag,
const AttributeList &));
MOCK_METHOD2(FindChildHandler, DIEHandler *(uint64, DwarfTag));
MOCK_METHOD0(Finish, void());
};
@@ -94,11 +92,10 @@ class MockRootDIEHandler: public RootDIEHandler {
MOCK_METHOD3(ProcessAttributeSignature,
void(DwarfAttribute, DwarfForm, uint64));
MOCK_METHOD0(EndAttributes, bool());
MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag,
const AttributeList &));
MOCK_METHOD2(FindChildHandler, DIEHandler *(uint64, DwarfTag));
MOCK_METHOD0(Finish, void());
MOCK_METHOD5(StartCompilationUnit, bool(uint64, uint8, uint8, uint64, uint8));
MOCK_METHOD3(StartRootDIE, bool(uint64, DwarfTag, const AttributeList &));
MOCK_METHOD2(StartRootDIE, bool(uint64, DwarfTag));
};
// If the handler elects to skip the compilation unit, the dispatcher
@@ -129,18 +126,13 @@ TEST(Dwarf2DIEHandler, SkipRootDIE) {
MockRootDIEHandler mock_root_handler;
DIEDispatcher die_dispatcher(&mock_root_handler);
AttributeList mock_attribute_list;
mock_attribute_list.push_back(make_pair(dwarf2reader::DW_AT_name,
dwarf2reader::DW_FORM_string));
EXPECT_CALL(mock_root_handler,
StartCompilationUnit(0xde8994029fc8b999LL, 0xf4, 0x02,
0xb00febffa76e2b2bLL, 0x5c))
.InSequence(s)
.WillOnce(Return(true));
EXPECT_CALL(mock_root_handler,
StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6,
ContainerEq(mock_attribute_list)))
StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6))
.InSequence(s)
.WillOnce(Return(false));
@@ -148,8 +140,7 @@ TEST(Dwarf2DIEHandler, SkipRootDIE) {
0xf4, 0x02,
0xb00febffa76e2b2bLL, 0x5c));
EXPECT_FALSE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL,
(DwarfTag) 0xb4f98da6,
mock_attribute_list));
(DwarfTag) 0xb4f98da6));
die_dispatcher.EndDIE(0x7d08242b4b510cf2LL);
}
@@ -160,8 +151,6 @@ TEST(Dwarf2DIEHandler, SkipRootDIEChildren) {
MockRootDIEHandler mock_root_handler;
DIEDispatcher die_dispatcher(&mock_root_handler);
AttributeList mock_attribute_list;
{
InSequence s;
@@ -170,8 +159,7 @@ TEST(Dwarf2DIEHandler, SkipRootDIEChildren) {
0x09f8bf0767f91675LL, 0xdb))
.WillOnce(Return(true));
EXPECT_CALL(mock_root_handler,
StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6,
ContainerEq(mock_attribute_list)))
StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6))
.WillOnce(Return(true));
// Please don't tell me about my children.
EXPECT_CALL(mock_root_handler, EndAttributes())
@@ -184,11 +172,9 @@ TEST(Dwarf2DIEHandler, SkipRootDIEChildren) {
0x26, 0xa0,
0x09f8bf0767f91675LL, 0xdb));
EXPECT_TRUE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL,
(DwarfTag) 0xb4f98da6,
mock_attribute_list));
(DwarfTag) 0xb4f98da6));
EXPECT_FALSE(die_dispatcher.StartDIE(0x435150ceedccda18LL,
(DwarfTag) 0xc3a17bba,
mock_attribute_list));
(DwarfTag) 0xc3a17bba));
die_dispatcher.EndDIE(0x435150ceedccda18LL);
die_dispatcher.EndDIE(0x7d08242b4b510cf2LL);
}
@@ -199,9 +185,6 @@ TEST(Dwarf2DIEHandler, PassAttributeValues) {
MockRootDIEHandler mock_root_handler;
DIEDispatcher die_dispatcher(&mock_root_handler);
AttributeList mock_attribute_list;
mock_attribute_list.push_back(make_pair(dwarf2reader::DW_AT_name,
dwarf2reader::DW_FORM_string));
const char buffer[10] = { 0x24, 0x24, 0x35, 0x9a, 0xca,
0xcf, 0xa8, 0x84, 0xa7, 0x18 };
string str = "\xc8\x26\x2e\x0d\xa4\x9c\x37\xd6\xfb\x1d";
@@ -218,8 +201,7 @@ TEST(Dwarf2DIEHandler, PassAttributeValues) {
// We'll like the root DIE.
EXPECT_CALL(mock_root_handler,
StartRootDIE(0xe2222da01e29f2a9LL, (DwarfTag) 0x9829445c,
ContainerEq(mock_attribute_list)))
StartRootDIE(0xe2222da01e29f2a9LL, (DwarfTag) 0x9829445c))
.WillOnce(Return(true));
// Expect some attribute values.
@@ -255,7 +237,7 @@ TEST(Dwarf2DIEHandler, PassAttributeValues) {
.WillOnce(Return());
EXPECT_CALL(mock_root_handler, EndAttributes())
.WillOnce(Return(true));
EXPECT_CALL(mock_root_handler, FindChildHandler(_, _, _))
EXPECT_CALL(mock_root_handler, FindChildHandler(_, _))
.Times(0);
EXPECT_CALL(mock_root_handler, Finish())
.WillOnce(Return());
@@ -270,8 +252,7 @@ TEST(Dwarf2DIEHandler, PassAttributeValues) {
0x66));
// Report the root DIE.
EXPECT_TRUE(die_dispatcher.StartDIE(0xe2222da01e29f2a9LL,
(DwarfTag) 0x9829445c,
mock_attribute_list));
(DwarfTag) 0x9829445c));
// Report some attribute values.
die_dispatcher.ProcessAttributeUnsigned(0xe2222da01e29f2a9LL,
@@ -309,25 +290,6 @@ TEST(Dwarf2DIEHandler, FindAndSkipChildren) {
MockDIEHandler *mock_child3_handler = new(MockDIEHandler);
DIEDispatcher die_dispatcher(&mock_root_handler);
AttributeList root_attribute_list;
root_attribute_list.push_back(make_pair((DwarfAttribute) 0xb01185df,
(DwarfForm) 0xbc97cee8));
AttributeList child1_attribute_list;
child1_attribute_list.push_back(make_pair((DwarfAttribute) 0x41014e43,
(DwarfForm) 0x63155f4c));
AttributeList grandchild1_attribute_list;
grandchild1_attribute_list.push_back(make_pair((DwarfAttribute) 0xf72f823c,
(DwarfForm) 0x0ff6a201));
AttributeList greatgrandchild1_attribute_list;
greatgrandchild1_attribute_list
.push_back(make_pair((DwarfAttribute) 0xbe66e5f0, (DwarfForm) 0xb4b24ff7));
AttributeList child2_attribute_list;
child1_attribute_list.push_back(make_pair((DwarfAttribute) 0xf22df14c,
(DwarfForm) 0x20676e7d));
AttributeList child3_attribute_list;
child3_attribute_list.push_back(make_pair((DwarfAttribute) 0xe8bf1201,
(DwarfForm) 0x53a5b7a8));
{
InSequence s;
@@ -340,8 +302,7 @@ TEST(Dwarf2DIEHandler, FindAndSkipChildren) {
// Root DIE.
{
EXPECT_CALL(mock_root_handler,
StartRootDIE(0x15f0e06bdfe3c372LL, (DwarfTag) 0xf5d60c59,
ContainerEq(root_attribute_list)))
StartRootDIE(0x15f0e06bdfe3c372LL, (DwarfTag) 0xf5d60c59))
.WillOnce(Return(true));
EXPECT_CALL(mock_root_handler,
ProcessAttributeSigned((DwarfAttribute) 0xf779a642,
@@ -354,8 +315,7 @@ TEST(Dwarf2DIEHandler, FindAndSkipChildren) {
// First child DIE.
EXPECT_CALL(mock_root_handler,
FindChildHandler(0x149f644f8116fe8cLL,
(DwarfTag) 0xac2cbd8c,
ContainerEq(child1_attribute_list)))
(DwarfTag) 0xac2cbd8c))
.WillOnce(Return(mock_child1_handler));
{
EXPECT_CALL(*mock_child1_handler,
@@ -374,15 +334,13 @@ TEST(Dwarf2DIEHandler, FindAndSkipChildren) {
// for this child.
EXPECT_CALL(mock_root_handler,
FindChildHandler(0x97412be24875de9dLL,
(DwarfTag) 0x505a068b,
ContainerEq(child2_attribute_list)))
(DwarfTag) 0x505a068b))
.WillOnce(Return((DIEHandler *) NULL));
// Third child DIE.
EXPECT_CALL(mock_root_handler,
FindChildHandler(0x753c964c8ab538aeLL,
(DwarfTag) 0x8c22970e,
ContainerEq(child3_attribute_list)))
(DwarfTag) 0x8c22970e))
.WillOnce(Return(mock_child3_handler));
{
EXPECT_CALL(*mock_child3_handler,
@@ -411,8 +369,7 @@ TEST(Dwarf2DIEHandler, FindAndSkipChildren) {
// Report the root DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0x15f0e06bdfe3c372LL,
(DwarfTag) 0xf5d60c59,
root_attribute_list));
(DwarfTag) 0xf5d60c59));
die_dispatcher.ProcessAttributeSigned(0x15f0e06bdfe3c372LL,
(DwarfAttribute) 0xf779a642,
(DwarfForm) 0x2cb63027,
@@ -421,8 +378,7 @@ TEST(Dwarf2DIEHandler, FindAndSkipChildren) {
// First child DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0x149f644f8116fe8cLL,
(DwarfTag) 0xac2cbd8c,
child1_attribute_list));
(DwarfTag) 0xac2cbd8c));
die_dispatcher.ProcessAttributeSigned(0x149f644f8116fe8cLL,
(DwarfAttribute) 0xa6fd6f65,
(DwarfForm) 0xe4f64c41,
@@ -431,15 +387,13 @@ TEST(Dwarf2DIEHandler, FindAndSkipChildren) {
// First grandchild DIE. Will be skipped.
{
EXPECT_FALSE(die_dispatcher.StartDIE(0xd68de1ee0bd29419LL,
(DwarfTag) 0x22f05a15,
grandchild1_attribute_list));
(DwarfTag) 0x22f05a15));
// First great-grandchild DIE. Will be skipped without being
// mentioned to any handler.
{
EXPECT_FALSE(die_dispatcher
.StartDIE(0xb3076285d25cac25LL,
(DwarfTag) 0xcff4061b,
greatgrandchild1_attribute_list));
(DwarfTag) 0xcff4061b));
die_dispatcher.EndDIE(0xb3076285d25cac25LL);
}
die_dispatcher.EndDIE(0xd68de1ee0bd29419LL);
@@ -450,16 +404,14 @@ TEST(Dwarf2DIEHandler, FindAndSkipChildren) {
// Second child DIE. Root handler will decline to find a handler for it.
{
EXPECT_FALSE(die_dispatcher.StartDIE(0x97412be24875de9dLL,
(DwarfTag) 0x505a068b,
child2_attribute_list));
(DwarfTag) 0x505a068b));
die_dispatcher.EndDIE(0x97412be24875de9dLL);
}
// Third child DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0x753c964c8ab538aeLL,
(DwarfTag) 0x8c22970e,
child3_attribute_list));
(DwarfTag) 0x8c22970e));
die_dispatcher.ProcessAttributeSigned(0x753c964c8ab538aeLL,
(DwarfAttribute) 0x4e2b7cfb,
(DwarfForm) 0x610b7ae1,
@@ -478,7 +430,6 @@ TEST(Dwarf2DIEHandler, FreeHandlersOnStack) {
MockRootDIEHandler mock_root_handler;
MockDIEHandler *mock_child_handler = new(MockDIEHandler);
MockDIEHandler *mock_grandchild_handler = new(MockDIEHandler);
AttributeList empty_attribute_list;
{
InSequence s;
@@ -492,8 +443,7 @@ TEST(Dwarf2DIEHandler, FreeHandlersOnStack) {
// Root DIE.
{
EXPECT_CALL(mock_root_handler,
StartRootDIE(0xbf13b761691ddc91LL, (DwarfTag) 0x98980361,
ContainerEq(empty_attribute_list)))
StartRootDIE(0xbf13b761691ddc91LL, (DwarfTag) 0x98980361))
.WillOnce(Return(true));
EXPECT_CALL(mock_root_handler, EndAttributes())
.WillOnce(Return(true));
@@ -501,8 +451,7 @@ TEST(Dwarf2DIEHandler, FreeHandlersOnStack) {
// Child DIE.
EXPECT_CALL(mock_root_handler,
FindChildHandler(0x058f09240c5fc8c9LL,
(DwarfTag) 0x898bf0d0,
ContainerEq(empty_attribute_list)))
(DwarfTag) 0x898bf0d0))
.WillOnce(Return(mock_child_handler));
{
EXPECT_CALL(*mock_child_handler, EndAttributes())
@@ -511,8 +460,7 @@ TEST(Dwarf2DIEHandler, FreeHandlersOnStack) {
// Grandchild DIE.
EXPECT_CALL(*mock_child_handler,
FindChildHandler(0x32dc00c9945dc0c8LL,
(DwarfTag) 0x2802d007,
ContainerEq(empty_attribute_list)))
(DwarfTag) 0x2802d007))
.WillOnce(Return(mock_grandchild_handler));
{
EXPECT_CALL(*mock_grandchild_handler,
@@ -548,20 +496,17 @@ TEST(Dwarf2DIEHandler, FreeHandlersOnStack) {
// Report the root DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0xbf13b761691ddc91LL,
(DwarfTag) 0x98980361,
empty_attribute_list));
(DwarfTag) 0x98980361));
// Child DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0x058f09240c5fc8c9LL,
(DwarfTag) 0x898bf0d0,
empty_attribute_list));
(DwarfTag) 0x898bf0d0));
// Grandchild DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0x32dc00c9945dc0c8LL,
(DwarfTag) 0x2802d007,
empty_attribute_list));
(DwarfTag) 0x2802d007));
die_dispatcher.ProcessAttributeSigned(0x32dc00c9945dc0c8LL,
(DwarfAttribute) 0x4e2b7cfb,
(DwarfForm) 0x610b7ae1,

View File

@@ -41,11 +41,13 @@
#include <map>
#include <memory>
#include <stack>
#include <string>
#include <utility>
#include "common/dwarf/bytereader-inl.h"
#include "common/dwarf/bytereader.h"
#include "common/dwarf/line_state_machine.h"
#include "common/using_std_string.h"
namespace dwarf2reader {
@@ -498,7 +500,7 @@ void CompilationUnit::ProcessDIEs() {
const Abbrev& abbrev = abbrevs_->at(static_cast<size_t>(abbrev_num));
const enum DwarfTag tag = abbrev.tag;
if (!handler_->StartDIE(absolute_offset, tag, abbrev.attributes)) {
if (!handler_->StartDIE(absolute_offset, tag)) {
dieptr = SkipDIE(dieptr, abbrev);
} else {
dieptr = ProcessDIE(absolute_offset, dieptr, abbrev);
@@ -1004,7 +1006,7 @@ class CallFrameInfo::RegisterRule: public CallFrameInfo::Rule {
// Rule: EXPRESSION evaluates to the address at which the register is saved.
class CallFrameInfo::ExpressionRule: public CallFrameInfo::Rule {
public:
explicit ExpressionRule(const std::string &expression)
explicit ExpressionRule(const string &expression)
: expression_(expression) { }
~ExpressionRule() { }
bool Handle(Handler *handler, uint64 address, int reg) const {
@@ -1018,13 +1020,13 @@ class CallFrameInfo::ExpressionRule: public CallFrameInfo::Rule {
}
Rule *Copy() const { return new ExpressionRule(*this); }
private:
std::string expression_;
string expression_;
};
// Rule: EXPRESSION evaluates to the address at which the register is saved.
class CallFrameInfo::ValExpressionRule: public CallFrameInfo::Rule {
public:
explicit ValExpressionRule(const std::string &expression)
explicit ValExpressionRule(const string &expression)
: expression_(expression) { }
~ValExpressionRule() { }
bool Handle(Handler *handler, uint64 address, int reg) const {
@@ -1039,7 +1041,7 @@ class CallFrameInfo::ValExpressionRule: public CallFrameInfo::Rule {
}
Rule *Copy() const { return new ValExpressionRule(*this); }
private:
std::string expression_;
string expression_;
};
// A map from register numbers to rules.
@@ -1220,7 +1222,7 @@ class CallFrameInfo::State {
unsigned register_number; // A register number.
uint64 offset; // An offset or address.
long signed_offset; // A signed offset.
std::string expression; // A DWARF expression.
string expression; // A DWARF expression.
};
// Parse CFI instruction operands from STATE's instruction stream as
@@ -1407,7 +1409,7 @@ bool CallFrameInfo::State::ParseOperands(const char *format,
if (len > bytes_left || expression_length > bytes_left - len)
return ReportIncomplete();
cursor_ += len;
operands->expression = std::string(cursor_, expression_length);
operands->expression = string(cursor_, expression_length);
cursor_ += expression_length;
break;
}
@@ -1510,16 +1512,19 @@ bool CallFrameInfo::State::DoInstruction() {
// Change the base register used to compute the CFA.
case DW_CFA_def_cfa_register: {
if (!ParseOperands("r", &ops)) return false;
Rule *cfa_rule = rules_.CFARule();
if (!cfa_rule) {
reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset());
if (!DoDefCFA(ops.register_number, ops.offset)) {
reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset());
return false;
}
} else {
cfa_rule->SetBaseRegister(ops.register_number);
if (!cfa_rule->Handle(handler_, address_,
Handler::kCFARegister))
return false;
}
if (!ParseOperands("r", &ops)) return false;
cfa_rule->SetBaseRegister(ops.register_number);
if (!cfa_rule->Handle(handler_, address_,
Handler::kCFARegister))
return false;
break;
}
@@ -1872,7 +1877,7 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
memchr(augmentation_start, '\0', cie->end - augmentation_start);
if (! augmentation_end) return ReportIncomplete(cie);
cursor = static_cast<const char *>(augmentation_end);
cie->augmentation = std::string(augmentation_start,
cie->augmentation = string(augmentation_start,
cursor - augmentation_start);
// Skip the terminating '\0'.
cursor++;
@@ -2260,7 +2265,7 @@ void CallFrameInfo::Reporter::UnrecognizedVersion(uint64 offset, int version) {
}
void CallFrameInfo::Reporter::UnrecognizedAugmentation(uint64 offset,
const std::string &aug) {
const string &aug) {
fprintf(stderr,
"%s: CFI frame description entry at offset 0x%llx in '%s':"
" CIE specifies unrecognized augmentation: '%s'\n",

View File

@@ -49,6 +49,7 @@
#include "common/dwarf/bytereader.h"
#include "common/dwarf/dwarf2enums.h"
#include "common/dwarf/types.h"
#include "common/using_std_string.h"
namespace dwarf2reader {
struct LineStateMachine;
@@ -57,7 +58,7 @@ class LineInfoHandler;
// This maps from a string naming a section to a pair containing a
// the data for the section, and the size of the section.
typedef std::map<std::string, std::pair<const char*, uint64> > SectionMap;
typedef std::map<string, std::pair<const char*, uint64> > SectionMap;
typedef std::list<std::pair<enum DwarfAttribute, enum DwarfForm> >
AttributeList;
typedef AttributeList::iterator AttributeIterator;
@@ -156,7 +157,7 @@ class LineInfoHandler {
// Called when we define a directory. NAME is the directory name,
// DIR_NUM is the directory number
virtual void DefineDir(const std::string& name, uint32 dir_num) { }
virtual void DefineDir(const string& name, uint32 dir_num) { }
// Called when we define a filename. NAME is the filename, FILE_NUM
// is the file number which is -1 if the file index is the next
@@ -165,7 +166,7 @@ class LineInfoHandler {
// directory index for the directory name of this file, MOD_TIME is
// the modification time of the file, and LENGTH is the length of
// the file
virtual void DefineFile(const std::string& name, int32 file_num,
virtual void DefineFile(const string& name, int32 file_num,
uint32 dir_num, uint64 mod_time,
uint64 length) { }
@@ -341,8 +342,7 @@ class Dwarf2Handler {
// Start to process a DIE at OFFSET from the beginning of the .debug_info
// section. Return false if you would like to skip this DIE.
virtual bool StartDIE(uint64 offset, enum DwarfTag tag,
const AttributeList& attrs) { return false; }
virtual bool StartDIE(uint64 offset, enum DwarfTag tag) { return false; }
// Called when we have an attribute with unsigned data to give to our
// handler. The attribute is for the DIE at OFFSET from the beginning of the
@@ -391,7 +391,7 @@ class Dwarf2Handler {
virtual void ProcessAttributeString(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const std::string& data) { }
const string& data) { }
// Called when we have an attribute whose value is the 64-bit signature
// of a type unit in the .debug_types section. OFFSET is the offset of
@@ -699,7 +699,7 @@ class CallFrameInfo {
// A common information entry (CIE).
struct CIE: public Entry {
uint8 version; // CFI data version number
std::string augmentation; // vendor format extension markers
string augmentation; // vendor format extension markers
uint64 code_alignment_factor; // scale for code address adjustments
int data_alignment_factor; // scale for stack pointer adjustments
unsigned return_address_register; // which register holds the return addr
@@ -833,7 +833,7 @@ class CallFrameInfo::Handler {
// process a given FDE, the parser reiterates the appropriate CIE's
// contents at the beginning of the FDE's rules.
virtual bool Entry(size_t offset, uint64 address, uint64 length,
uint8 version, const std::string &augmentation,
uint8 version, const string &augmentation,
unsigned return_address) = 0;
// When the Entry function returns true, the parser calls these
@@ -882,13 +882,13 @@ class CallFrameInfo::Handler {
// At ADDRESS, the DWARF expression EXPRESSION yields the address at
// which REG was saved.
virtual bool ExpressionRule(uint64 address, int reg,
const std::string &expression) = 0;
const string &expression) = 0;
// At ADDRESS, the DWARF expression EXPRESSION yields the caller's
// value for REG. (This rule doesn't provide an address at which the
// register's value is saved.)
virtual bool ValExpressionRule(uint64 address, int reg,
const std::string &expression) = 0;
const string &expression) = 0;
// Indicate that the rules for the address range reported by the
// last call to Entry are complete. End should return true if
@@ -965,8 +965,8 @@ class CallFrameInfo::Reporter {
// in a Mach-O section named __debug_frame. If we support
// Linux-style exception handling data, we could be reading an
// .eh_frame section.
Reporter(const std::string &filename,
const std::string &section = ".debug_frame")
Reporter(const string &filename,
const string &section = ".debug_frame")
: filename_(filename), section_(section) { }
virtual ~Reporter() { }
@@ -998,7 +998,7 @@ class CallFrameInfo::Reporter {
// which we don't recognize. We cannot parse DWARF CFI if it uses
// augmentations we don't recognize.
virtual void UnrecognizedAugmentation(uint64 offset,
const std::string &augmentation);
const string &augmentation);
// The pointer encoding ENCODING, specified by the CIE at OFFSET, is not
// a valid encoding.
@@ -1039,10 +1039,10 @@ class CallFrameInfo::Reporter {
protected:
// The name of the file whose CFI we're reading.
std::string filename_;
string filename_;
// The name of the CFI section in that file.
std::string section_;
string section_;
};
} // namespace dwarf2reader

View File

@@ -62,6 +62,7 @@ extern "C" {
#include "common/dwarf/bytereader-inl.h"
#include "common/dwarf/cfi_assembler.h"
#include "common/dwarf/dwarf2reader.h"
#include "common/using_std_string.h"
#include "google_breakpad/common/breakpad_types.h"
using google_breakpad::CFISection;
@@ -76,7 +77,6 @@ using dwarf2reader::ENDIANNESS_LITTLE;
using dwarf2reader::ByteReader;
using dwarf2reader::CallFrameInfo;
using std::string;
using std::vector;
using testing::InSequence;
using testing::Return;
@@ -2326,14 +2326,14 @@ struct ELFSectionHeader {
alignment(1), entry_size(0) { }
Label name;
unsigned int type;
u_int64_t flags;
u_int64_t address;
uint64_t flags;
uint64_t address;
Label file_offset;
Label file_size;
unsigned int link;
unsigned int info;
u_int64_t alignment;
u_int64_t entry_size;
uint64_t alignment;
uint64_t entry_size;
};
void AppendSectionHeader(CFISection *table, const ELFSectionHeader &header) {

View File

@@ -41,6 +41,7 @@
#include "common/dwarf/bytereader-inl.h"
#include "common/dwarf/dwarf2reader_test_common.h"
#include "common/dwarf/dwarf2reader.h"
#include "common/using_std_string.h"
#include "google_breakpad/common/breakpad_types.h"
using google_breakpad::test_assembler::Endianness;
@@ -49,7 +50,6 @@ using google_breakpad::test_assembler::Section;
using google_breakpad::test_assembler::kBigEndian;
using google_breakpad::test_assembler::kLittleEndian;
using dwarf2reader::AttributeList;
using dwarf2reader::ByteReader;
using dwarf2reader::CompilationUnit;
using dwarf2reader::Dwarf2Handler;
@@ -61,7 +61,6 @@ using dwarf2reader::ENDIANNESS_BIG;
using dwarf2reader::ENDIANNESS_LITTLE;
using dwarf2reader::SectionMap;
using std::string;
using std::vector;
using testing::InSequence;
using testing::Pointee;
@@ -76,8 +75,7 @@ class MockDwarf2Handler: public Dwarf2Handler {
MOCK_METHOD5(StartCompilationUnit, bool(uint64 offset, uint8 address_size,
uint8 offset_size, uint64 cu_length,
uint8 dwarf_version));
MOCK_METHOD3(StartDIE, bool(uint64 offset, enum DwarfTag tag,
const AttributeList& attrs));
MOCK_METHOD2(StartDIE, bool(uint64 offset, enum DwarfTag tag));
MOCK_METHOD4(ProcessAttributeUnsigned, void(uint64 offset,
DwarfAttribute attr,
enum DwarfForm form,
@@ -98,7 +96,7 @@ class MockDwarf2Handler: public Dwarf2Handler {
MOCK_METHOD4(ProcessAttributeString, void(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const std::string& data));
const string& data));
MOCK_METHOD4(ProcessAttributeSignature, void(uint64 offset,
DwarfAttribute attr,
enum DwarfForm form,
@@ -115,7 +113,7 @@ struct DIEFixture {
// Default expectations for the data handler.
EXPECT_CALL(handler, StartCompilationUnit(_, _, _, _, _)).Times(0);
EXPECT_CALL(handler, StartDIE(_, _, _)).Times(0);
EXPECT_CALL(handler, StartDIE(_, _)).Times(0);
EXPECT_CALL(handler, ProcessAttributeUnsigned(_, _, _, _)).Times(0);
EXPECT_CALL(handler, ProcessAttributeSigned(_, _, _, _)).Times(0);
EXPECT_CALL(handler, ProcessAttributeReference(_, _, _, _)).Times(0);
@@ -186,7 +184,7 @@ TEST_P(DwarfHeader, Header) {
GetParam().format_size, _,
GetParam().version))
.WillOnce(Return(true));
EXPECT_CALL(handler, StartDIE(_, dwarf2reader::DW_TAG_compile_unit, _))
EXPECT_CALL(handler, StartDIE(_, dwarf2reader::DW_TAG_compile_unit))
.WillOnce(Return(true));
EXPECT_CALL(handler, ProcessAttributeString(_, dwarf2reader::DW_AT_name,
dwarf2reader::DW_FORM_string,
@@ -262,7 +260,7 @@ struct DwarfFormsFixture: public DIEFixture {
params.version))
.InSequence(s)
.WillOnce(Return(true));
EXPECT_CALL(handler, StartDIE(_, tag, _))
EXPECT_CALL(handler, StartDIE(_, tag))
.InSequence(s)
.WillOnce(Return(true));
}
@@ -291,7 +289,7 @@ TEST_P(DwarfForms, addr) {
StartSingleAttributeDIE(GetParam(), dwarf2reader::DW_TAG_compile_unit,
dwarf2reader::DW_AT_low_pc,
dwarf2reader::DW_FORM_addr);
u_int64_t value;
uint64_t value;
if (GetParam().address_size == 4) {
value = 0xc8e9ffcc;
info.D32(value);
@@ -374,7 +372,7 @@ TEST_P(DwarfForms, sec_offset) {
StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x1d971689,
(DwarfAttribute) 0xa060bfd1,
dwarf2reader::DW_FORM_sec_offset);
u_int64_t value;
uint64_t value;
if (GetParam().format_size == 4) {
value = 0xacc9c388;
info.D32(value);

View File

@@ -97,7 +97,7 @@ class TestCompilationUnit: public google_breakpad::test_assembler::Section {
// The offset of the point in the compilation unit header immediately
// after the initial length field.
u_int64_t post_length_offset_;
uint64_t post_length_offset_;
// The length of the compilation unit, not including the initial length field.
Label length_;

View File

@@ -36,17 +36,18 @@
#include <map>
#include <queue>
#include <vector>
#include <memory>
#include "common/dwarf/functioninfo.h"
#include "common/dwarf/bytereader.h"
#include "common/scoped_ptr.h"
#include "common/using_std_string.h"
using google_breakpad::scoped_ptr;
namespace dwarf2reader {
CULineInfoHandler::CULineInfoHandler(std::vector<SourceFileInfo>* files,
std::vector<std::string>* dirs,
std::vector<string>* dirs,
LineMap* linemap):linemap_(linemap),
files_(files),
dirs_(dirs) {
@@ -61,13 +62,13 @@ CULineInfoHandler::CULineInfoHandler(std::vector<SourceFileInfo>* files,
files->push_back(s);
}
void CULineInfoHandler::DefineDir(const std::string& name, uint32 dir_num) {
void CULineInfoHandler::DefineDir(const string& name, uint32 dir_num) {
// These should never come out of order, actually
assert(dir_num == dirs_->size());
dirs_->push_back(name);
}
void CULineInfoHandler::DefineFile(const std::string& name,
void CULineInfoHandler::DefineFile(const string& name,
int32 file_num, uint32 dir_num,
uint64 mod_time, uint64 length) {
assert(dir_num >= 0);
@@ -75,7 +76,7 @@ void CULineInfoHandler::DefineFile(const std::string& name,
// These should never come out of order, actually.
if (file_num == (int32)files_->size() || file_num == -1) {
std::string dir = dirs_->at(dir_num);
string dir = dirs_->at(dir_num);
SourceFileInfo s;
s.lowpc = ULLONG_MAX;
@@ -100,11 +101,11 @@ void CULineInfoHandler::AddLine(uint64 address, uint64 length, uint32 file_num,
std::make_pair(files_->at(file_num).name.c_str(),
line_num)));
if(address < files_->at(file_num).lowpc) {
if (address < files_->at(file_num).lowpc) {
files_->at(file_num).lowpc = address;
}
} else {
fprintf(stderr,"error in AddLine");
fprintf(stderr, "error in AddLine");
}
}
@@ -122,8 +123,7 @@ bool CUFunctionInfoHandler::StartCompilationUnit(uint64 offset,
// subroutines. For line info, the DW_AT_stmt_list lives in the
// compile unit tag.
bool CUFunctionInfoHandler::StartDIE(uint64 offset, enum DwarfTag tag,
const AttributeList& attrs) {
bool CUFunctionInfoHandler::StartDIE(uint64 offset, enum DwarfTag tag) {
switch (tag) {
case DW_TAG_subprogram:
case DW_TAG_inlined_subroutine: {
@@ -149,11 +149,11 @@ bool CUFunctionInfoHandler::StartDIE(uint64 offset, enum DwarfTag tag,
void CUFunctionInfoHandler::ProcessAttributeString(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const std::string &data) {
const string &data) {
if (current_function_info_) {
if (attr == DW_AT_name)
current_function_info_->name = data;
else if(attr == DW_AT_MIPS_linkage_name)
else if (attr == DW_AT_MIPS_linkage_name)
current_function_info_->mangled_name = data;
}
}
@@ -166,10 +166,9 @@ void CUFunctionInfoHandler::ProcessAttributeUnsigned(uint64 offset,
SectionMap::const_iterator iter = sections_.find("__debug_line");
assert(iter != sections_.end());
// this should be a scoped_ptr but we dont' use boost :-(
std::auto_ptr<LineInfo> lireader(new LineInfo(iter->second.first + data,
iter->second.second - data,
reader_, linehandler_));
scoped_ptr<LineInfo> lireader(new LineInfo(iter->second.first + data,
iter->second.second - data,
reader_, linehandler_));
lireader->Start();
} else if (current_function_info_) {
switch (attr) {
@@ -210,7 +209,10 @@ void CUFunctionInfoHandler::ProcessAttributeReference(uint64 offset,
current_function_info_->mangled_name = iter->second->mangled_name;
} else {
// If you hit this, this code probably needs to be rewritten.
fprintf(stderr, "Error: DW_AT_specification was seen before the referenced DIE! (Looking for DIE at offset %08llx, in DIE at offset %08llx)\n", data, offset);
fprintf(stderr,
"Error: DW_AT_specification was seen before the referenced "
"DIE! (Looking for DIE at offset %08llx, in DIE at "
"offset %08llx)\n", data, offset);
}
break;
}

View File

@@ -40,17 +40,18 @@
#include <vector>
#include "common/dwarf/dwarf2reader.h"
#include "common/using_std_string.h"
namespace dwarf2reader {
struct FunctionInfo {
// Name of the function
std::string name;
string name;
// Mangled name of the function
std::string mangled_name;
string mangled_name;
// File containing this function
std::string file;
string file;
// Line number for start of function.
uint32 line;
// Beginning address for this function
@@ -61,13 +62,13 @@ struct FunctionInfo {
struct SourceFileInfo {
// Name of the source file name
std::string name;
string name;
// Low address of source file name
uint64 lowpc;
};
typedef std::map<uint64, FunctionInfo*> FunctionMap;
typedef std::map<uint64, std::pair<std::string, uint32> > LineMap;
typedef std::map<uint64, std::pair<string, uint32> > LineMap;
// This class is a basic line info handler that fills in the dirs,
// file, and linemap passed into it with the data produced from the
@@ -77,17 +78,17 @@ class CULineInfoHandler: public LineInfoHandler {
//
CULineInfoHandler(std::vector<SourceFileInfo>* files,
std::vector<std::string>* dirs,
std::vector<string>* dirs,
LineMap* linemap);
virtual ~CULineInfoHandler() { }
// Called when we define a directory. We just place NAME into dirs_
// at position DIR_NUM.
virtual void DefineDir(const std::string& name, uint32 dir_num);
virtual void DefineDir(const string& name, uint32 dir_num);
// Called when we define a filename. We just place
// concat(dirs_[DIR_NUM], NAME) into files_ at position FILE_NUM.
virtual void DefineFile(const std::string& name, int32 file_num,
virtual void DefineFile(const string& name, int32 file_num,
uint32 dir_num, uint64 mod_time, uint64 length);
@@ -103,13 +104,13 @@ class CULineInfoHandler: public LineInfoHandler {
private:
LineMap* linemap_;
std::vector<SourceFileInfo>* files_;
std::vector<std::string>* dirs_;
std::vector<string>* dirs_;
};
class CUFunctionInfoHandler: public Dwarf2Handler {
public:
CUFunctionInfoHandler(std::vector<SourceFileInfo>* files,
std::vector<std::string>* dirs,
std::vector<string>* dirs,
LineMap* linemap,
FunctionMap* offset_to_funcinfo,
FunctionMap* address_to_funcinfo,
@@ -134,8 +135,7 @@ class CUFunctionInfoHandler: public Dwarf2Handler {
// Start to process a DIE at OFFSET from the beginning of the
// .debug_info section. We only care about function related DIE's.
virtual bool StartDIE(uint64 offset, enum DwarfTag tag,
const AttributeList& attrs);
virtual bool StartDIE(uint64 offset, enum DwarfTag tag);
// Called when we have an attribute with unsigned data to give to
// our handler. The attribute is for the DIE at OFFSET from the
@@ -163,7 +163,7 @@ class CUFunctionInfoHandler: public Dwarf2Handler {
virtual void ProcessAttributeString(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const std::string& data);
const string& data);
// Called when finished processing the DIE at OFFSET.
// Because DWARF2/3 specifies a tree of DIEs, you may get starts
@@ -173,7 +173,7 @@ class CUFunctionInfoHandler: public Dwarf2Handler {
private:
std::vector<SourceFileInfo>* files_;
std::vector<std::string>* dirs_;
std::vector<string>* dirs_;
LineMap* linemap_;
FunctionMap* offset_to_funcinfo_;
FunctionMap* address_to_funcinfo_;

View File

@@ -84,17 +84,44 @@ vector<string> DwarfCFIToModule::RegisterNames::X86_64() {
return MakeVector(names, sizeof(names) / sizeof(names[0]));
}
// Per ARM IHI 0040A, section 3.1
vector<string> DwarfCFIToModule::RegisterNames::ARM() {
static const char *const names[] = {
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
"fps", "cpsr"
"fps", "cpsr", "", "", "", "", "", "",
"", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "",
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
"s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15",
"s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23",
"s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31",
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7"
};
return MakeVector(names, sizeof(names) / sizeof(names[0]));
}
vector<string> DwarfCFIToModule::RegisterNames::MIPS() {
static const char* const kRegisterNames[] = {
"$zero", "$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3",
"$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7",
"$s0", "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7",
"$t8", "$t9", "$k0", "$k1", "$gp", "$sp", "$fp", "$ra",
"$lo", "$hi", "$pc", "$f0", "$f2", "$f3", "$f4", "$f5",
"$f6", "$f7", "$f8", "$f9", "$f10", "$f11", "$f12", "$f13",
"$f14", "$f15", "$f16", "$f17", "$f18", "$f19", "$f20",
"$f21", "$f22", "$f23", "$f24", "$f25", "$f26", "$f27",
"$f28", "$f29", "$f30", "$f31", "$fcsr", "$fir"
};
return MakeVector(kRegisterNames,
sizeof(kRegisterNames) / sizeof(kRegisterNames[0]));
}
bool DwarfCFIToModule::Entry(size_t offset, uint64 address, uint64 length,
uint8 version, const string &augmentation,
unsigned return_address) {
@@ -132,7 +159,8 @@ string DwarfCFIToModule::RegisterName(int i) {
if (reg == return_address_)
return ra_name_;
if (reg < register_names_.size())
// Ensure that a non-empty name exists for this register value.
if (reg < register_names_.size() && !register_names_[reg].empty())
return register_names_[reg];
reporter_->UnnamedRegister(entry_offset_, reg);

View File

@@ -48,13 +48,13 @@
#include "common/module.h"
#include "common/dwarf/dwarf2reader.h"
#include "common/using_std_string.h"
namespace google_breakpad {
using dwarf2reader::CallFrameInfo;
using google_breakpad::Module;
using std::set;
using std::string;
using std::vector;
// A class that accepts parsed call frame information from the DWARF
@@ -108,6 +108,9 @@ class DwarfCFIToModule: public CallFrameInfo::Handler {
// ARM.
static vector<string> ARM();
// MIPS.
static vector<string> MIPS();
private:
// Given STRINGS, an array of C strings with SIZE elements, return an

View File

@@ -36,8 +36,8 @@
#include "breakpad_googletest_includes.h"
#include "common/dwarf_cfi_to_module.h"
#include "common/using_std_string.h"
using std::string;
using std::vector;
using google_breakpad::Module;
@@ -69,6 +69,7 @@ struct DwarfCFIToModuleFixture {
register_names.push_back("reg7");
register_names.push_back("sp");
register_names.push_back("pc");
register_names.push_back("");
EXPECT_CALL(reporter, UnnamedRegister(_, _)).Times(0);
EXPECT_CALL(reporter, UndefinedNotSupported(_, _)).Times(0);
@@ -140,6 +141,17 @@ TEST_F(Rule, UndefinedRule) {
EXPECT_EQ(0U, entries[0]->rule_changes.size());
}
TEST_F(Rule, RegisterWithEmptyName) {
EXPECT_CALL(reporter, UnnamedRegister(_, 10));
EXPECT_CALL(reporter, UndefinedNotSupported(_, "unnamed_register10"));
StartEntry();
ASSERT_TRUE(handler.UndefinedRule(entry_address, 10));
ASSERT_TRUE(handler.End());
CheckEntry();
EXPECT_EQ(0U, entries[0]->initial_rules.size());
EXPECT_EQ(0U, entries[0]->rule_changes.size());
}
TEST_F(Rule, SameValueRule) {
StartEntry();
ASSERT_TRUE(handler.SameValueRule(entry_address, 6));
@@ -178,17 +190,17 @@ TEST_F(Rule, OffsetRuleNegative) {
TEST_F(Rule, ValOffsetRule) {
// Use an unnamed register number, to exercise that branch of RegisterName.
EXPECT_CALL(reporter, UnnamedRegister(_, 10));
EXPECT_CALL(reporter, UnnamedRegister(_, 11));
StartEntry();
ASSERT_TRUE(handler.ValOffsetRule(entry_address + 0x5ab7,
DwarfCFIToModule::kCFARegister,
10, 61812979));
11, 61812979));
ASSERT_TRUE(handler.End());
CheckEntry();
EXPECT_EQ(0U, entries[0]->initial_rules.size());
Module::RuleChangeMap expected_changes;
expected_changes[entry_address + 0x5ab7][".cfa"] =
"unnamed_register10 61812979 +";
"unnamed_register11 61812979 +";
EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
}

View File

@@ -39,6 +39,9 @@
#include "common/dwarf_cu_to_module.h"
#include <assert.h>
#if !defined(__ANDROID__)
#include <cxxabi.h>
#endif
#include <inttypes.h>
#include <stdio.h>
@@ -53,10 +56,11 @@ namespace google_breakpad {
using std::map;
using std::pair;
using std::set;
using std::sort;
using std::vector;
// Data provided by a DWARF specification DIE.
//
//
// In DWARF, the DIE for a definition may contain a DW_AT_specification
// attribute giving the offset of the corresponding declaration DIE, and
// the definition DIE may omit information given in the declaration. For
@@ -73,6 +77,9 @@ using std::vector;
// A Specification holds information gathered from a declaration DIE that
// we may need if we find a DW_AT_specification link pointing to it.
struct DwarfCUToModule::Specification {
// The qualified name that can be found by demangling DW_AT_MIPS_linkage_name.
string qualified_name;
// The name of the enclosing scope, or the empty string if there is none.
string enclosing_name;
@@ -84,7 +91,7 @@ struct DwarfCUToModule::Specification {
// An abstract origin -- base definition of an inline function.
struct AbstractOrigin {
AbstractOrigin() : name() {}
AbstractOrigin(const string& name) : name(name) {}
explicit AbstractOrigin(const string& name) : name(name) {}
string name;
};
@@ -97,12 +104,20 @@ struct DwarfCUToModule::FilePrivate {
// A set of strings used in this CU. Before storing a string in one of
// our data structures, insert it into this set, and then use the string
// from the set.
//
// Because std::string uses reference counting internally, simply using
// strings from this set, even if passed by value, assigned, or held
// directly in structures and containers (map<string, ...>, for example),
// causes those strings to share a single instance of each distinct piece
// of text.
//
// In some STL implementations, strings are reference-counted internally,
// meaning that simply using strings from this set, even if passed by
// value, assigned, or held directly in structures and containers
// (map<string, ...>, for example), causes those strings to share a
// single instance of each distinct piece of text. GNU's libstdc++ uses
// reference counts, and I believe MSVC did as well, at some point.
// However, C++ '11 implementations are moving away from reference
// counting.
//
// In other implementations, string assignments copy the string's text,
// so this set will actually hold yet another copy of the string (although
// everything will still work). To improve memory consumption portably,
// we will probably need to use pointers to strings held in this set.
set<string> common_strings;
// A map from offsets of DIEs within the .debug_info section to
@@ -113,14 +128,42 @@ struct DwarfCUToModule::FilePrivate {
AbstractOriginByOffset origins;
};
DwarfCUToModule::FileContext::FileContext(const string &filename_arg,
Module *module_arg)
: filename(filename_arg), module(module_arg) {
file_private = new FilePrivate();
DwarfCUToModule::FileContext::FileContext(const string &filename,
Module *module,
bool handle_inter_cu_refs)
: filename_(filename),
module_(module),
handle_inter_cu_refs_(handle_inter_cu_refs),
file_private_(new FilePrivate()) {
}
DwarfCUToModule::FileContext::~FileContext() {
delete file_private;
}
void DwarfCUToModule::FileContext::AddSectionToSectionMap(
const string& name, const char* contents, uint64 length) {
section_map_[name] = std::make_pair(contents, length);
}
void DwarfCUToModule::FileContext::ClearSectionMapForTest() {
section_map_.clear();
}
const dwarf2reader::SectionMap&
DwarfCUToModule::FileContext::section_map() const {
return section_map_;
}
void DwarfCUToModule::FileContext::ClearSpecifications() {
if (!handle_inter_cu_refs_)
file_private_->specifications.clear();
}
bool DwarfCUToModule::FileContext::IsUnhandledInterCUReference(
uint64 offset, uint64 compilation_unit_start) const {
if (handle_inter_cu_refs_)
return false;
return offset < compilation_unit_start;
}
// Information global to the particular compilation unit we're
@@ -130,11 +173,13 @@ struct DwarfCUToModule::CUContext {
CUContext(FileContext *file_context_arg, WarningReporter *reporter_arg)
: file_context(file_context_arg),
reporter(reporter_arg),
language(Language::CPlusPlus) { }
language(Language::CPlusPlus) {}
~CUContext() {
for (vector<Module::Function *>::iterator it = functions.begin();
it != functions.end(); it++)
it != functions.end(); ++it) {
delete *it;
}
};
// The DWARF-bearing file into which this CU was incorporated.
@@ -218,6 +263,14 @@ class DwarfCUToModule::GenericDIEHandler: public dwarf2reader::DIEHandler {
DIEContext *parent_context_;
uint64 offset_;
// Place the name in the global set of strings. Even though this looks
// like a copy, all the major std::string implementations use reference
// counting internally, so the effect is to have all the data structures
// share copies of strings whenever possible.
// FIXME: Should this return something like a string_ref to avoid the
// assumption about how strings are implemented?
string AddStringToPool(const string &str);
// If this DIE has a DW_AT_declaration attribute, this is its value.
// It is false on DIEs with no DW_AT_declaration attribute.
bool declaration_;
@@ -230,6 +283,11 @@ class DwarfCUToModule::GenericDIEHandler: public dwarf2reader::DIEHandler {
// The value of the DW_AT_name attribute, or the empty string if the
// DIE has no such attribute.
string name_attribute_;
// The demangled value of the DW_AT_MIPS_linkage_name attribute, or the empty
// string if the DIE has no such attribute or its content could not be
// demangled.
string demangled_name_;
};
void DwarfCUToModule::GenericDIEHandler::ProcessAttributeUnsigned(
@@ -248,14 +306,19 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeReference(
uint64 data) {
switch (attr) {
case dwarf2reader::DW_AT_specification: {
FileContext *file_context = cu_context_->file_context;
if (file_context->IsUnhandledInterCUReference(
data, cu_context_->reporter->cu_offset())) {
cu_context_->reporter->UnhandledInterCUReference(offset_, data);
break;
}
// Find the Specification to which this attribute refers, and
// set specification_ appropriately. We could do more processing
// here, but it's better to leave the real work to our
// EndAttribute member function, at which point we know we have
// seen all the DIE's attributes.
FileContext *file_context = cu_context_->file_context;
SpecificationByOffset *specifications
= &file_context->file_private->specifications;
SpecificationByOffset *specifications =
&file_context->file_private_->specifications;
SpecificationByOffset::iterator spec = specifications->find(data);
if (spec != specifications->end()) {
specification_ = &spec->second;
@@ -273,20 +336,29 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeReference(
}
}
string DwarfCUToModule::GenericDIEHandler::AddStringToPool(const string &str) {
pair<set<string>::iterator, bool> result =
cu_context_->file_context->file_private_->common_strings.insert(str);
return *result.first;
}
void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString(
enum DwarfAttribute attr,
enum DwarfForm form,
const string &data) {
switch (attr) {
case dwarf2reader::DW_AT_name: {
// Place the name in our global set of strings, and then use the
// string from the set. Even though the assignment looks like a copy,
// all the major std::string implementations use reference counting
// internally, so the effect is to have all our data structures share
// copies of strings whenever possible.
pair<set<string>::iterator, bool> result =
cu_context_->file_context->file_private->common_strings.insert(data);
name_attribute_ = *result.first;
case dwarf2reader::DW_AT_name:
name_attribute_ = AddStringToPool(data);
break;
case dwarf2reader::DW_AT_MIPS_linkage_name: {
char* demangled = NULL;
#if !defined(__ANDROID__)
demangled = abi::__cxa_demangle(data.c_str(), NULL, NULL, NULL);
#endif
if (demangled) {
demangled_name_ = AddStringToPool(demangled);
free(reinterpret_cast<void*>(demangled));
}
break;
}
default: break;
@@ -294,33 +366,53 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString(
}
string DwarfCUToModule::GenericDIEHandler::ComputeQualifiedName() {
// Find our unqualified name. If the DIE has its own DW_AT_name
// attribute, then use that; otherwise, check our specification.
const string *unqualified_name;
if (name_attribute_.empty() && specification_)
unqualified_name = &specification_->unqualified_name;
else
unqualified_name = &name_attribute_;
// Use the demangled name, if one is available. Demangled names are
// preferable to those inferred from the DWARF structure because they
// include argument types.
const string *qualified_name = NULL;
if (!demangled_name_.empty()) {
// Found it is this DIE.
qualified_name = &demangled_name_;
} else if (specification_ && !specification_->qualified_name.empty()) {
// Found it on the specification.
qualified_name = &specification_->qualified_name;
}
// Find the name of our enclosing context. If we have a
// specification, it's the specification's enclosing context that
// counts; otherwise, use this DIE's context.
const string *unqualified_name;
const string *enclosing_name;
if (specification_)
enclosing_name = &specification_->enclosing_name;
else
enclosing_name = &parent_context_->name;
if (!qualified_name) {
// Find our unqualified name. If the DIE has its own DW_AT_name
// attribute, then use that; otherwise, check our specification.
if (name_attribute_.empty() && specification_)
unqualified_name = &specification_->unqualified_name;
else
unqualified_name = &name_attribute_;
// Find the name of our enclosing context. If we have a
// specification, it's the specification's enclosing context that
// counts; otherwise, use this DIE's context.
if (specification_)
enclosing_name = &specification_->enclosing_name;
else
enclosing_name = &parent_context_->name;
}
// If this DIE was marked as a declaration, record its names in the
// specification table.
if (declaration_) {
FileContext *file_context = cu_context_->file_context;
Specification spec;
spec.enclosing_name = *enclosing_name;
spec.unqualified_name = *unqualified_name;
file_context->file_private->specifications[offset_] = spec;
if (qualified_name) {
spec.qualified_name = *qualified_name;
} else {
spec.enclosing_name = *enclosing_name;
spec.unqualified_name = *unqualified_name;
}
cu_context_->file_context->file_private_->specifications[offset_] = spec;
}
if (qualified_name)
return *qualified_name;
// Combine the enclosing name and unqualified name to produce our
// own fully-qualified name.
return cu_context_->language->MakeQualifiedName(*enclosing_name,
@@ -333,7 +425,8 @@ class DwarfCUToModule::FuncHandler: public GenericDIEHandler {
FuncHandler(CUContext *cu_context, DIEContext *parent_context,
uint64 offset)
: GenericDIEHandler(cu_context, parent_context, offset),
low_pc_(0), high_pc_(0), abstract_origin_(NULL), inline_(false) { }
low_pc_(0), high_pc_(0), high_pc_form_(dwarf2reader::DW_FORM_addr),
abstract_origin_(NULL), inline_(false) { }
void ProcessAttributeUnsigned(enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data);
@@ -352,6 +445,7 @@ class DwarfCUToModule::FuncHandler: public GenericDIEHandler {
// specification_, parent_context_. Computed in EndAttributes.
string name_;
uint64 low_pc_, high_pc_; // DW_AT_low_pc, DW_AT_high_pc
DwarfForm high_pc_form_; // DW_AT_high_pc can be length or address.
const AbstractOrigin* abstract_origin_;
bool inline_;
};
@@ -367,7 +461,11 @@ void DwarfCUToModule::FuncHandler::ProcessAttributeUnsigned(
case dwarf2reader::DW_AT_inline: inline_ = true; break;
case dwarf2reader::DW_AT_low_pc: low_pc_ = data; break;
case dwarf2reader::DW_AT_high_pc: high_pc_ = data; break;
case dwarf2reader::DW_AT_high_pc:
high_pc_form_ = form;
high_pc_ = data;
break;
default:
GenericDIEHandler::ProcessAttributeUnsigned(attr, form, data);
break;
@@ -393,10 +491,10 @@ void DwarfCUToModule::FuncHandler::ProcessAttributeReference(
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) {
switch(attr) {
switch (attr) {
case dwarf2reader::DW_AT_abstract_origin: {
const AbstractOriginByOffset& origins =
cu_context_->file_context->file_private->origins;
cu_context_->file_context->file_private_->origins;
AbstractOriginByOffset::const_iterator origin = origins.find(data);
if (origin != origins.end()) {
abstract_origin_ = &(origin->second);
@@ -421,6 +519,11 @@ bool DwarfCUToModule::FuncHandler::EndAttributes() {
}
void DwarfCUToModule::FuncHandler::Finish() {
// Make high_pc_ an address, if it isn't already.
if (high_pc_form_ != dwarf2reader::DW_FORM_addr) {
high_pc_ += low_pc_;
}
// Did we collect the information we need? Not all DWARF function
// entries have low and high addresses (for example, inlined
// functions that were never used), but all the ones we're
@@ -447,7 +550,7 @@ void DwarfCUToModule::FuncHandler::Finish() {
}
} else if (inline_) {
AbstractOrigin origin(name_);
cu_context_->file_context->file_private->origins[offset_] = origin;
cu_context_->file_context->file_private_->origins[offset_] = origin;
}
}
@@ -459,8 +562,7 @@ class DwarfCUToModule::NamedScopeHandler: public GenericDIEHandler {
uint64 offset)
: GenericDIEHandler(cu_context, parent_context, offset) { }
bool EndAttributes();
DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag,
const AttributeList &attrs);
DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag);
private:
DIEContext child_context_; // A context for our children.
@@ -473,8 +575,7 @@ bool DwarfCUToModule::NamedScopeHandler::EndAttributes() {
dwarf2reader::DIEHandler *DwarfCUToModule::NamedScopeHandler::FindChildHandler(
uint64 offset,
enum DwarfTag tag,
const AttributeList &attrs) {
enum DwarfTag tag) {
switch (tag) {
case dwarf2reader::DW_TAG_subprogram:
return new FuncHandler(cu_context_, &child_context_, offset);
@@ -561,17 +662,25 @@ void DwarfCUToModule::WarningReporter::UnnamedFunction(uint64 offset) {
filename_.c_str(), offset);
}
void DwarfCUToModule::WarningReporter::UnhandledInterCUReference(
uint64 offset, uint64 target) {
CUHeading();
fprintf(stderr, "%s: warning: the DIE at offset 0x%llx has a "
"DW_FORM_ref_addr attribute with an inter-CU reference to "
"0x%llx, but inter-CU reference handling is turned off.\n",
filename_.c_str(), offset, target);
}
DwarfCUToModule::DwarfCUToModule(FileContext *file_context,
LineToModuleFunctor *line_reader,
LineToModuleHandler *line_reader,
WarningReporter *reporter)
: line_reader_(line_reader), has_source_line_info_(false) {
cu_context_ = new CUContext(file_context, reporter);
child_context_ = new DIEContext();
: line_reader_(line_reader),
cu_context_(new CUContext(file_context, reporter)),
child_context_(new DIEContext()),
has_source_line_info_(false) {
}
DwarfCUToModule::~DwarfCUToModule() {
delete cu_context_;
delete child_context_;
}
void DwarfCUToModule::ProcessAttributeSigned(enum DwarfAttribute attr,
@@ -605,8 +714,16 @@ void DwarfCUToModule::ProcessAttributeUnsigned(enum DwarfAttribute attr,
void DwarfCUToModule::ProcessAttributeString(enum DwarfAttribute attr,
enum DwarfForm form,
const string &data) {
if (attr == dwarf2reader::DW_AT_name)
cu_context_->reporter->SetCUName(data);
switch (attr) {
case dwarf2reader::DW_AT_name:
cu_context_->reporter->SetCUName(data);
break;
case dwarf2reader::DW_AT_comp_dir:
line_reader_->StartCompilationUnit(data);
break;
default:
break;
}
}
bool DwarfCUToModule::EndAttributes() {
@@ -615,16 +732,16 @@ bool DwarfCUToModule::EndAttributes() {
dwarf2reader::DIEHandler *DwarfCUToModule::FindChildHandler(
uint64 offset,
enum DwarfTag tag,
const AttributeList &attrs) {
enum DwarfTag tag) {
switch (tag) {
case dwarf2reader::DW_TAG_subprogram:
return new FuncHandler(cu_context_, child_context_, offset);
return new FuncHandler(cu_context_.get(), child_context_.get(), offset);
case dwarf2reader::DW_TAG_namespace:
case dwarf2reader::DW_TAG_class_type:
case dwarf2reader::DW_TAG_structure_type:
case dwarf2reader::DW_TAG_union_type:
return new NamedScopeHandler(cu_context_, child_context_, offset);
return new NamedScopeHandler(cu_context_.get(), child_context_.get(),
offset);
default:
return NULL;
}
@@ -649,7 +766,7 @@ void DwarfCUToModule::SetLanguage(DwarfLanguage language) {
// Objective C and Objective C++ seem to create entries for
// methods whose DW_AT_name values are already fully-qualified:
// "-[Classname method:]". These appear at the top level.
//
//
// DWARF data for C should never include namespaces or functions
// nested in struct types, but if it ever does, then C++'s
// notation is probably not a bad choice for that.
@@ -667,7 +784,7 @@ void DwarfCUToModule::SetLanguage(DwarfLanguage language) {
void DwarfCUToModule::ReadSourceLines(uint64 offset) {
const dwarf2reader::SectionMap &section_map
= cu_context_->file_context->section_map;
= cu_context_->file_context->section_map();
dwarf2reader::SectionMap::const_iterator map_entry
= section_map.find(".debug_line");
// Mac OS X puts DWARF data in sections whose names begin with "__"
@@ -684,8 +801,8 @@ void DwarfCUToModule::ReadSourceLines(uint64 offset) {
cu_context_->reporter->BadLineInfoOffset(offset);
return;
}
(*line_reader_)(section_start + offset, section_length - offset,
cu_context_->file_context->module, &lines_);
line_reader_->ReadProgram(section_start + offset, section_length - offset,
cu_context_->file_context->module_, &lines_);
}
namespace {
@@ -715,9 +832,9 @@ void DwarfCUToModule::AssignLinesToFunctions() {
// complexity from here on out is linear.
// Put both our functions and lines in order by address.
sort(functions->begin(), functions->end(),
Module::Function::CompareByAddress);
sort(lines_.begin(), lines_.end(), Module::Line::CompareByAddress);
std::sort(functions->begin(), functions->end(),
Module::Function::CompareByAddress);
std::sort(lines_.begin(), lines_.end(), Module::Line::CompareByAddress);
// The last line that we used any piece of. We use this only for
// generating warnings.
@@ -852,7 +969,7 @@ void DwarfCUToModule::AssignLinesToFunctions() {
// both func and line begin after CURRENT. The next transition
// is the start of the next function or next line, whichever
// is earliest.
assert (func || line);
assert(func || line);
if (func && line)
next_transition = std::min(func->address, line->address);
else if (func)
@@ -910,12 +1027,14 @@ void DwarfCUToModule::Finish() {
// Add our functions, which now have source lines assigned to them,
// to module_.
cu_context_->file_context->module->AddFunctions(functions->begin(),
functions->end());
cu_context_->file_context->module_->AddFunctions(functions->begin(),
functions->end());
// Ownership of the function objects has shifted from cu_context to
// the Module.
functions->clear();
cu_context_->file_context->ClearSpecifications();
}
bool DwarfCUToModule::StartCompilationUnit(uint64 offset,
@@ -926,8 +1045,7 @@ bool DwarfCUToModule::StartCompilationUnit(uint64 offset,
return dwarf_version >= 2;
}
bool DwarfCUToModule::StartRootDIE(uint64 offset, enum DwarfTag tag,
const AttributeList& attrs) {
bool DwarfCUToModule::StartRootDIE(uint64 offset, enum DwarfTag tag) {
// We don't deal with partial compilation units (the only other tag
// likely to be used for root DIE).
return tag == dwarf2reader::DW_TAG_compile_unit;

View File

@@ -46,10 +46,11 @@
#include "common/dwarf/bytereader.h"
#include "common/dwarf/dwarf2diehandler.h"
#include "common/dwarf/dwarf2reader.h"
#include "common/scoped_ptr.h"
#include "common/using_std_string.h"
namespace google_breakpad {
using dwarf2reader::AttributeList;
using dwarf2reader::DwarfAttribute;
using dwarf2reader::DwarfForm;
using dwarf2reader::DwarfLanguage;
@@ -65,7 +66,6 @@ using dwarf2reader::DwarfTag;
class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
struct FilePrivate;
public:
// Information global to the DWARF-bearing file we are processing,
// for use by DwarfCUToModule. Each DwarfCUToModule instance deals
// with a single compilation unit within the file, but information
@@ -73,40 +73,75 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
// for filling it in appropriately (except for the 'file_private'
// field, which the constructor and destructor take care of), and
// then providing it to the DwarfCUToModule instance for each
// compilation unit we process in that file.
struct FileContext {
FileContext(const string &filename_arg, Module *module_arg);
// compilation unit we process in that file. Set HANDLE_INTER_CU_REFS
// to true to handle debugging symbols with DW_FORM_ref_addr entries.
class FileContext {
public:
FileContext(const string &filename,
Module *module,
bool handle_inter_cu_refs);
~FileContext();
// Add CONTENTS of size LENGTH to the section map as NAME.
void AddSectionToSectionMap(const string& name,
const char* contents,
uint64 length);
// Clear the section map for testing.
void ClearSectionMapForTest();
const dwarf2reader::SectionMap& section_map() const;
private:
friend class DwarfCUToModule;
// Clears all the Specifications if HANDLE_INTER_CU_REFS_ is false.
void ClearSpecifications();
// Given an OFFSET and a CU that starts at COMPILATION_UNIT_START, returns
// true if this is an inter-compilation unit reference that is not being
// handled.
bool IsUnhandledInterCUReference(uint64 offset,
uint64 compilation_unit_start) const;
// The name of this file, for use in error messages.
string filename;
const string filename_;
// A map of this file's sections, used for finding other DWARF
// sections that the .debug_info section may refer to.
dwarf2reader::SectionMap section_map;
dwarf2reader::SectionMap section_map_;
// The Module to which we're contributing definitions.
Module *module;
Module *module_;
// True if we are handling references between compilation units.
const bool handle_inter_cu_refs_;
// Inter-compilation unit data used internally by the handlers.
FilePrivate *file_private;
scoped_ptr<FilePrivate> file_private_;
};
// An abstract base class for functors that handle DWARF line data
// An abstract base class for handlers that handle DWARF line data
// for DwarfCUToModule. DwarfCUToModule could certainly just use
// dwarf2reader::LineInfo itself directly, but decoupling things
// this way makes unit testing a little easier.
class LineToModuleFunctor {
class LineToModuleHandler {
public:
LineToModuleFunctor() { }
virtual ~LineToModuleFunctor() { }
LineToModuleHandler() { }
virtual ~LineToModuleHandler() { }
// Called at the beginning of a new compilation unit, prior to calling
// ReadProgram(). compilation_dir will indicate the path that the
// current compilation unit was compiled in, consistent with the
// DW_AT_comp_dir DIE.
virtual void StartCompilationUnit(const string& compilation_dir) = 0;
// Populate MODULE and LINES with source file names and code/line
// mappings, given a pointer to some DWARF line number data
// PROGRAM, and an overestimate of its size. Add no zero-length
// lines to LINES.
virtual void operator()(const char *program, uint64 length,
Module *module, vector<Module::Line> *lines) = 0;
virtual void ReadProgram(const char *program, uint64 length,
Module *module, vector<Module::Line> *lines) = 0;
};
// The interface DwarfCUToModule uses to report warnings. The member
@@ -164,9 +199,17 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
// link.
virtual void UnnamedFunction(uint64 offset);
// The DW_FORM_ref_addr at OFFSET to TARGET was not handled because
// FilePrivate did not retain the inter-CU specification data.
virtual void UnhandledInterCUReference(uint64 offset, uint64 target);
uint64 cu_offset() const {
return cu_offset_;
}
protected:
string filename_;
uint64 cu_offset_;
const string filename_;
const uint64 cu_offset_;
string cu_name_;
bool printed_cu_header_;
bool printed_unpaired_header_;
@@ -186,7 +229,7 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
// unit's line number data. Use REPORTER to report problems with the
// data we find.
DwarfCUToModule(FileContext *file_context,
LineToModuleFunctor *line_reader,
LineToModuleHandler *line_reader,
WarningReporter *reporter);
~DwarfCUToModule();
@@ -200,8 +243,7 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
enum DwarfForm form,
const string &data);
bool EndAttributes();
DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag,
const AttributeList &attrs);
DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag);
// Assign all our source Lines to the Functions that cover their
// addresses, and then add them to module_.
@@ -210,17 +252,14 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
bool StartCompilationUnit(uint64 offset, uint8 address_size,
uint8 offset_size, uint64 cu_length,
uint8 dwarf_version);
bool StartRootDIE(uint64 offset, enum DwarfTag tag,
const AttributeList& attrs);
bool StartRootDIE(uint64 offset, enum DwarfTag tag);
private:
// Used internally by the handler. Full definitions are in
// dwarf_cu_to_module.cc.
struct FilePrivate;
struct Specification;
struct CUContext;
struct DIEContext;
struct Specification;
class GenericDIEHandler;
class FuncHandler;
class NamedScopeHandler;
@@ -230,7 +269,7 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
// Set this compilation unit's source language to LANGUAGE.
void SetLanguage(DwarfLanguage language);
// Read source line information at OFFSET in the .debug_line
// section. Record source files in module_, but record source lines
// in lines_; we apportion them to functions in
@@ -249,14 +288,14 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
// owned by this DwarfCUToModule: the constructor sets them, and the
// destructor deletes them.
// The functor to use to handle line number data.
LineToModuleFunctor *line_reader_;
// The handler to use to handle line number data.
LineToModuleHandler *line_reader_;
// This compilation unit's context.
CUContext *cu_context_;
scoped_ptr<CUContext> cu_context_;
// A context for our children.
DIEContext *child_context_;
scoped_ptr<DIEContext> child_context_;
// True if this compilation unit has source line information.
bool has_source_line_info_;
@@ -271,6 +310,6 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
vector<Module::Line> lines_;
};
} // namespace google_breakpad
} // namespace google_breakpad
#endif // COMMON_LINUX_DWARF_CU_TO_MODULE_H__

File diff suppressed because it is too large Load Diff

View File

@@ -32,38 +32,45 @@
// dwarf_line_to_module.cc: Implementation of DwarfLineToModule class.
// See dwarf_line_to_module.h for details.
#include "common/dwarf_line_to_module.h"
#include <stdio.h>
#include <string>
#include "common/dwarf_line_to_module.h"
#include "common/using_std_string.h"
// Trying to support Windows paths in a reasonable way adds a lot of
// variations to test; it would be better to just put off dealing with
// it until we actually have to deal with DWARF on Windows.
// Return true if PATH is an absolute path, false if it is relative.
static bool PathIsAbsolute(const std::string &path) {
static bool PathIsAbsolute(const string &path) {
return (path.size() >= 1 && path[0] == '/');
}
static bool HasTrailingSlash(const string &path) {
return (path.size() >= 1 && path[path.size() - 1] == '/');
}
// If PATH is an absolute path, return PATH. If PATH is a relative path,
// treat it as relative to BASE and return the combined path.
static std::string ExpandPath(const std::string &path,
const std::string &base) {
if (PathIsAbsolute(path))
static string ExpandPath(const string &path,
const string &base) {
if (PathIsAbsolute(path) || base.empty())
return path;
return base + "/" + path;
return base + (HasTrailingSlash(base) ? "" : "/") + path;
}
namespace google_breakpad {
void DwarfLineToModule::DefineDir(const std::string &name, uint32 dir_num) {
void DwarfLineToModule::DefineDir(const string &name, uint32 dir_num) {
// Directory number zero is reserved to mean the compilation
// directory. Silently ignore attempts to redefine it.
if (dir_num != 0)
directories_[dir_num] = name;
directories_[dir_num] = ExpandPath(name, compilation_dir_);
}
void DwarfLineToModule::DefineFile(const std::string &name, int32 file_num,
void DwarfLineToModule::DefineFile(const string &name, int32 file_num,
uint32 dir_num, uint64 mod_time,
uint64 length) {
if (file_num == -1)
@@ -71,25 +78,26 @@ void DwarfLineToModule::DefineFile(const std::string &name, int32 file_num,
else if (file_num > highest_file_number_)
highest_file_number_ = file_num;
std::string full_name;
if (dir_num != 0) {
string dir_name;
if (dir_num == 0) {
// Directory number zero is the compilation directory, and is stored as
// an attribute on the compilation unit, rather than in the program table.
dir_name = compilation_dir_;
} else {
DirectoryTable::const_iterator directory_it = directories_.find(dir_num);
if (directory_it != directories_.end()) {
full_name = ExpandPath(name, directory_it->second);
dir_name = directory_it->second;
} else {
if (!warned_bad_directory_number_) {
fprintf(stderr, "warning: DWARF line number data refers to undefined"
" directory numbers\n");
warned_bad_directory_number_ = true;
}
full_name = name; // just treat name as relative
}
} else {
// Directory number zero is the compilation directory; we just report
// relative paths in that case.
full_name = name;
}
string full_name = ExpandPath(name, dir_name);
// Find a Module::File object of the given name, and add it to the
// file table.
files_[file_num] = module_->FindFile(full_name);

View File

@@ -38,8 +38,11 @@
#ifndef COMMON_LINUX_DWARF_LINE_TO_MODULE_H
#define COMMON_LINUX_DWARF_LINE_TO_MODULE_H
#include <string>
#include "common/module.h"
#include "common/dwarf/dwarf2reader.h"
#include "common/using_std_string.h"
namespace google_breakpad {
@@ -117,8 +120,10 @@ class DwarfLineToModule: public dwarf2reader::LineInfoHandler {
// end of the address space, we clip it. It's up to our client to
// sort out which lines belong to which functions; we don't add them
// to any particular function in MODULE ourselves.
DwarfLineToModule(Module *module, vector<Module::Line> *lines)
DwarfLineToModule(Module *module, const string& compilation_dir,
vector<Module::Line> *lines)
: module_(module),
compilation_dir_(compilation_dir),
lines_(lines),
highest_file_number_(-1),
omitted_line_end_(0),
@@ -127,8 +132,8 @@ class DwarfLineToModule: public dwarf2reader::LineInfoHandler {
~DwarfLineToModule() { }
void DefineDir(const std::string &name, uint32 dir_num);
void DefineFile(const std::string &name, int32 file_num,
void DefineDir(const string &name, uint32 dir_num);
void DefineFile(const string &name, int32 file_num,
uint32 dir_num, uint64 mod_time,
uint64 length);
void AddLine(uint64 address, uint64 length,
@@ -136,13 +141,17 @@ class DwarfLineToModule: public dwarf2reader::LineInfoHandler {
private:
typedef std::map<uint32, std::string> DirectoryTable;
typedef std::map<uint32, string> DirectoryTable;
typedef std::map<uint32, Module::File *> FileTable;
// The module we're contributing debugging info to. Owned by our
// client.
Module *module_;
// The compilation directory for the current compilation unit whose
// lines are being accumulated.
string compilation_dir_;
// The vector of lines we're accumulating. Owned by our client.
//
// In a Module, as in a breakpad symbol file, lines belong to

View File

@@ -45,7 +45,7 @@ using google_breakpad::Module;
TEST(SimpleModule, One) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
DwarfLineToModule h(&m, "/", &lines);
h.DefineFile("file1", 0x30bf0f27, 0, 0, 0);
h.AddLine(0x6fd126fbf74f2680LL, 0x63c9a14cf556712bLL, 0x30bf0f27,
@@ -54,7 +54,7 @@ TEST(SimpleModule, One) {
vector<Module::File *> files;
m.GetFiles(&files);
EXPECT_EQ(1U, files.size());
EXPECT_STREQ("file1", files[0]->name.c_str());
EXPECT_STREQ("/file1", files[0]->name.c_str());
EXPECT_EQ(1U, lines.size());
EXPECT_EQ(0x6fd126fbf74f2680ULL, lines[0].address);
@@ -66,7 +66,7 @@ TEST(SimpleModule, One) {
TEST(SimpleModule, Many) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
DwarfLineToModule h(&m, "/", &lines);
h.DefineDir("directory1", 0x838299ab);
h.DefineDir("directory2", 0xf85de023);
@@ -89,11 +89,11 @@ TEST(SimpleModule, Many) {
vector<Module::File *> files;
m.GetFiles(&files);
ASSERT_EQ(5U, files.size());
EXPECT_STREQ("directory1/file1", files[0]->name.c_str());
EXPECT_STREQ("directory1/file2", files[1]->name.c_str());
EXPECT_STREQ("directory2/file1", files[2]->name.c_str());
EXPECT_STREQ("directory2/file2", files[3]->name.c_str());
EXPECT_STREQ("file3", files[4]->name.c_str());
EXPECT_STREQ("/directory1/file1", files[0]->name.c_str());
EXPECT_STREQ("/directory1/file2", files[1]->name.c_str());
EXPECT_STREQ("/directory2/file1", files[2]->name.c_str());
EXPECT_STREQ("/directory2/file2", files[3]->name.c_str());
EXPECT_STREQ("/file3", files[4]->name.c_str());
ASSERT_EQ(5U, lines.size());
@@ -126,7 +126,7 @@ TEST(SimpleModule, Many) {
TEST(Filenames, Absolute) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
DwarfLineToModule h(&m, "/", &lines);
h.DefineDir("directory1", 1);
h.DefineFile("/absolute", 1, 1, 0, 0);
@@ -144,7 +144,7 @@ TEST(Filenames, Absolute) {
TEST(Filenames, Relative) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
DwarfLineToModule h(&m, "/", &lines);
h.DefineDir("directory1", 1);
h.DefineFile("relative", 1, 1, 0, 0);
@@ -154,7 +154,7 @@ TEST(Filenames, Relative) {
vector<Module::File *> files;
m.GetFiles(&files);
ASSERT_EQ(1U, files.size());
EXPECT_STREQ("directory1/relative", files[0]->name.c_str());
EXPECT_STREQ("/directory1/relative", files[0]->name.c_str());
ASSERT_EQ(1U, lines.size());
EXPECT_TRUE(lines[0].file == files[0]);
}
@@ -162,20 +162,20 @@ TEST(Filenames, Relative) {
TEST(Filenames, StrangeFile) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
DwarfLineToModule h(&m, "/", &lines);
h.DefineDir("directory1", 1);
h.DefineFile("", 1, 1, 0, 0);
h.AddLine(1, 1, 1, 0, 0);
ASSERT_EQ(1U, lines.size());
EXPECT_STREQ("directory1/", lines[0].file->name.c_str());
EXPECT_STREQ("/directory1/", lines[0].file->name.c_str());
}
TEST(Filenames, StrangeDirectory) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
DwarfLineToModule h(&m, "/", &lines);
h.DefineDir("", 1);
h.DefineFile("file1", 1, 1, 0, 0);
@@ -188,7 +188,7 @@ TEST(Filenames, StrangeDirectory) {
TEST(Filenames, StrangeDirectoryAndFile) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
DwarfLineToModule h(&m, "/", &lines);
h.DefineDir("", 1);
h.DefineFile("", 1, 1, 0, 0);
@@ -198,12 +198,60 @@ TEST(Filenames, StrangeDirectoryAndFile) {
EXPECT_STREQ("/", lines[0].file->name.c_str());
}
// We should use the compilation directory when encountering a file for
// directory number zero.
TEST(Filenames, DirectoryZeroFileIsRelativeToCompilationDir) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, "src/build", &lines);
h.DefineDir("Dir", 1);
h.DefineFile("File", 1, 0, 0, 0);
h.AddLine(1, 1, 1, 0, 0);
ASSERT_EQ(1U, lines.size());
EXPECT_STREQ("src/build/File", lines[0].file->name.c_str());
}
// We should treat non-absolute directories as relative to the compilation
// directory.
TEST(Filenames, IncludeDirectoryRelativeToDirectoryZero) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, "src/build", &lines);
h.DefineDir("Dir", 1);
h.DefineFile("File", 1, 1, 0, 0);
h.AddLine(1, 1, 1, 0, 0);
ASSERT_EQ(1U, lines.size());
EXPECT_STREQ("src/build/Dir/File", lines[0].file->name.c_str());
}
// We should treat absolute directories as absolute, and not relative to
// the compilation dir.
TEST(Filenames, IncludeDirectoryAbsolute) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, "src/build", &lines);
h.DefineDir("/Dir", 1);
h.DefineFile("File", 1, 1, 0, 0);
h.AddLine(1, 1, 1, 0, 0);
ASSERT_EQ(1U, lines.size());
EXPECT_STREQ("/Dir/File", lines[0].file->name.c_str());
}
// We should silently ignore attempts to define directory number zero,
// since that is always the compilation directory.
TEST(ModuleErrors, DirectoryZero) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
DwarfLineToModule h(&m, "/", &lines);
h.DefineDir("directory0", 0); // should be ignored
h.DefineFile("relative", 1, 0, 0, 0);
@@ -211,7 +259,7 @@ TEST(ModuleErrors, DirectoryZero) {
h.AddLine(1, 1, 1, 0, 0);
ASSERT_EQ(1U, lines.size());
EXPECT_STREQ("relative", lines[0].file->name.c_str());
EXPECT_STREQ("/relative", lines[0].file->name.c_str());
}
// We should refuse to add lines with bogus file numbers. We should
@@ -219,7 +267,7 @@ TEST(ModuleErrors, DirectoryZero) {
TEST(ModuleErrors, BadFileNumber) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
DwarfLineToModule h(&m, "/", &lines);
h.DefineFile("relative", 1, 0, 0, 0);
h.AddLine(1, 1, 2, 0, 0); // bad file number
@@ -233,7 +281,7 @@ TEST(ModuleErrors, BadFileNumber) {
TEST(ModuleErrors, BadDirectoryNumber) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
DwarfLineToModule h(&m, "/", &lines);
h.DefineDir("directory1", 1);
h.DefineFile("baddirnumber1", 1, 2, 0, 0); // bad directory number
@@ -248,7 +296,7 @@ TEST(ModuleErrors, BadDirectoryNumber) {
TEST(ModuleErrors, EmptyLine) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
DwarfLineToModule h(&m, "/", &lines);
h.DefineFile("filename1", 1, 0, 0, 0);
h.AddLine(1, 0, 1, 0, 0);
@@ -261,7 +309,7 @@ TEST(ModuleErrors, EmptyLine) {
TEST(ModuleErrors, BigLine) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
DwarfLineToModule h(&m, "/", &lines);
h.DefineFile("filename1", 1, 0, 0, 0);
h.AddLine(0xffffffffffffffffULL, 2, 1, 0, 0);
@@ -278,7 +326,7 @@ TEST(ModuleErrors, BigLine) {
TEST(Omitted, DroppedThenGood) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
DwarfLineToModule h(&m, "/", &lines);
h.DefineFile("filename1", 1, 0, 0, 0);
h.AddLine(0, 10, 1, 83816211, 0); // should be omitted
@@ -291,7 +339,7 @@ TEST(Omitted, DroppedThenGood) {
TEST(Omitted, GoodThenDropped) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
DwarfLineToModule h(&m, "/", &lines);
h.DefineFile("filename1", 1, 0, 0, 0);
h.AddLine(0x9dd6a372, 10, 1, 41454594, 0); // should be recorded
@@ -304,7 +352,7 @@ TEST(Omitted, GoodThenDropped) {
TEST(Omitted, Mix1) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
DwarfLineToModule h(&m, "/", &lines);
h.DefineFile("filename1", 1, 0, 0, 0);
h.AddLine(0x679ed72f, 10, 1, 58932642, 0); // should be recorded
@@ -325,7 +373,7 @@ TEST(Omitted, Mix1) {
TEST(Omitted, Mix2) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
DwarfLineToModule h(&m, "/", &lines);
h.DefineFile("filename1", 1, 0, 0, 0);
h.AddLine(0, 0xf2, 1, 58802211, 0); // should be omitted

View File

@@ -27,10 +27,9 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "common/linux/http_upload.h"
#include "common/http_upload.h"
#include <assert.h>
#include <dlfcn.h>
#include "third_party/curl/curl.h"
namespace {
@@ -41,7 +40,7 @@ static size_t WriteCallback(void *ptr, size_t size,
if (!userp)
return 0;
std::string *response = reinterpret_cast<std::string *>(userp);
string *response = reinterpret_cast<string *>(userp);
size_t real_size = size * nmemb;
response->append(reinterpret_cast<char *>(ptr), real_size);
return real_size;
@@ -70,40 +69,13 @@ bool HTTPUpload::SendRequest(const string &url,
if (!CheckParameters(parameters))
return false;
void *curl_lib = dlopen("libcurl.so", RTLD_NOW);
if (!curl_lib) {
if (error_description != NULL)
*error_description = dlerror();
curl_lib = dlopen("libcurl.so.4", RTLD_NOW);
}
if (!curl_lib) {
// Debian gives libcurl a different name when it is built against GnuTLS
// instead of OpenSSL.
curl_lib = dlopen("libcurl-gnutls.so.4", RTLD_NOW);
}
if (!curl_lib) {
curl_lib = dlopen("libcurl.so.3", RTLD_NOW);
}
if (!curl_lib) {
return false;
}
CURL* (*curl_easy_init)(void);
*(void**) (&curl_easy_init) = dlsym(curl_lib, "curl_easy_init");
CURL *curl = (*curl_easy_init)();
CURL *curl = curl_easy_init();
if (error_description != NULL)
*error_description = "No Error";
if (!curl) {
dlclose(curl_lib);
return false;
}
CURLcode err_code = CURLE_OK;
CURLcode (*curl_easy_setopt)(CURL *, CURLoption, ...);
*(void**) (&curl_easy_setopt) = dlsym(curl_lib, "curl_easy_setopt");
(*curl_easy_setopt)(curl, CURLOPT_URL, url.c_str());
(*curl_easy_setopt)(curl, CURLOPT_USERAGENT, kUserAgent);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_USERAGENT, kUserAgent);
// Set proxy information if necessary.
if (!proxy.empty())
(*curl_easy_setopt)(curl, CURLOPT_PROXY, proxy.c_str());
@@ -116,73 +88,57 @@ bool HTTPUpload::SendRequest(const string &url,
struct curl_httppost *formpost = NULL;
struct curl_httppost *lastptr = NULL;
// Add form data.
CURLFORMcode (*curl_formadd)(struct curl_httppost **, struct curl_httppost **, ...);
*(void**) (&curl_formadd) = dlsym(curl_lib, "curl_formadd");
map<string, string>::const_iterator iter = parameters.begin();
for (; iter != parameters.end(); ++iter)
(*curl_formadd)(&formpost, &lastptr,
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, iter->first.c_str(),
CURLFORM_COPYCONTENTS, iter->second.c_str(),
CURLFORM_END);
// Add form file.
(*curl_formadd)(&formpost, &lastptr,
curl_formadd(&formpost, &lastptr,
CURLFORM_COPYNAME, file_part_name.c_str(),
CURLFORM_FILE, upload_file.c_str(),
CURLFORM_END);
(*curl_easy_setopt)(curl, CURLOPT_HTTPPOST, formpost);
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
// Disable 100-continue header.
struct curl_slist *headerlist = NULL;
char buf[] = "Expect:";
struct curl_slist* (*curl_slist_append)(struct curl_slist *, const char *);
*(void**) (&curl_slist_append) = dlsym(curl_lib, "curl_slist_append");
headerlist = (*curl_slist_append)(headerlist, buf);
(*curl_easy_setopt)(curl, CURLOPT_HTTPHEADER, headerlist);
headerlist = curl_slist_append(headerlist, buf);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
if (response_body != NULL) {
(*curl_easy_setopt)(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
(*curl_easy_setopt)(curl, CURLOPT_WRITEDATA,
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA,
reinterpret_cast<void *>(response_body));
}
// Fail if 400+ is returned from the web server.
(*curl_easy_setopt)(curl, CURLOPT_FAILONERROR, 1);
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
CURLcode (*curl_easy_perform)(CURL *);
*(void**) (&curl_easy_perform) = dlsym(curl_lib, "curl_easy_perform");
err_code = (*curl_easy_perform)(curl);
err_code = curl_easy_perform(curl);
if (response_code != NULL) {
CURLcode (*curl_easy_getinfo)(CURL *, CURLINFO, ...);
*(void**) (&curl_easy_getinfo) = dlsym(curl_lib, "curl_easy_getinfo");
(*curl_easy_getinfo)(curl, CURLINFO_RESPONSE_CODE, response_code);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, response_code);
}
const char* (*curl_easy_strerror)(CURLcode);
*(void**) (&curl_easy_strerror) = dlsym(curl_lib, "curl_easy_strerror");
#ifndef NDEBUG
if (err_code != CURLE_OK)
fprintf(stderr, "Failed to send http request to %s, error: %s\n",
url.c_str(),
(*curl_easy_strerror)(err_code));
curl_easy_strerror(err_code));
#endif
if (error_description != NULL)
*error_description = (*curl_easy_strerror)(err_code);
*error_description = curl_easy_strerror(err_code);
void (*curl_easy_cleanup)(CURL *);
*(void**) (&curl_easy_cleanup) = dlsym(curl_lib, "curl_easy_cleanup");
(*curl_easy_cleanup)(curl);
curl_easy_cleanup(curl);
if (formpost != NULL) {
void (*curl_formfree)(struct curl_httppost *);
*(void**) (&curl_formfree) = dlsym(curl_lib, "curl_formfree");
(*curl_formfree)(formpost);
curl_formfree(formpost);
}
if (headerlist != NULL) {
void (*curl_slist_free_all)(struct curl_slist *);
*(void**) (&curl_slist_free_all) = dlsym(curl_lib, "curl_slist_free_all");
(*curl_slist_free_all)(headerlist);
curl_slist_free_all(headerlist);
}
dlclose(curl_lib);
return err_code == CURLE_OK;
}

View File

@@ -37,9 +37,10 @@
#include <map>
#include <string>
#include "common/using_std_string.h"
namespace google_breakpad {
using std::string;
using std::map;
class HTTPUpload {

View File

@@ -40,9 +40,9 @@
#include <string>
namespace google_breakpad {
#include "common/using_std_string.h"
using std::string;
namespace google_breakpad {
// An abstract base class for language-specific operations. We choose
// an instance of a subclass of this when we find the CU's language.
@@ -50,6 +50,10 @@ using std::string;
// language.
class Language {
public:
// A base class destructor should be either public and virtual,
// or protected and nonvirtual.
virtual ~Language() {}
// Return true if this language has functions to which we can assign
// line numbers. (Debugging info for assembly language, for example,
// can have source location information, but does not have functions

File diff suppressed because it is too large Load Diff

View File

@@ -37,20 +37,44 @@
#include <iostream>
#include <string>
#include <vector>
#include "common/symbol_data.h"
#include "common/using_std_string.h"
namespace google_breakpad {
class Module;
struct DumpOptions {
DumpOptions(SymbolData symbol_data, bool handle_inter_cu_refs)
: symbol_data(symbol_data),
handle_inter_cu_refs(handle_inter_cu_refs) {
}
SymbolData symbol_data;
bool handle_inter_cu_refs;
};
// Find all the debugging information in OBJ_FILE, an ELF executable
// or shared library, and write it to SYM_STREAM in the Breakpad symbol
// file format.
// If OBJ_FILE has been stripped but contains a .gnu_debuglink section,
// then look for the debug file in DEBUG_DIR.
// If CFI is set to false, then omit the CFI section.
bool WriteSymbolFile(const std::string &obj_file,
const std::string &debug_dir,
bool cfi,
// then look for the debug file in DEBUG_DIRS.
// SYMBOL_DATA allows limiting the type of symbol data written.
bool WriteSymbolFile(const string &obj_file,
const std::vector<string>& debug_dirs,
const DumpOptions& options,
std::ostream &sym_stream);
// As above, but simply return the debugging information in MODULE
// instead of writing it to a stream. The caller owns the resulting
// Module object and must delete it when finished.
bool ReadSymbolData(const string& obj_file,
const std::vector<string>& debug_dirs,
const DumpOptions& options,
Module** module);
} // namespace google_breakpad
#endif // COMMON_LINUX_DUMP_SYMBOLS_H__

View File

@@ -37,27 +37,27 @@
#include <stdio.h>
#include <sstream>
#include <string>
#include <vector>
#include "breakpad_googletest_includes.h"
#include "common/linux/dump_symbols.h"
#include "common/linux/synth_elf.h"
#include "common/module.h"
#include "common/using_std_string.h"
namespace google_breakpad {
bool WriteSymbolFileInternal(uint8_t* obj_file,
const std::string &obj_filename,
const std::string &debug_dir,
bool cfi,
std::ostream &sym_stream);
}
bool ReadSymbolDataInternal(const uint8_t* obj_file,
const string& obj_filename,
const std::vector<string>& debug_dir,
const DumpOptions& options,
Module** module);
using google_breakpad::synth_elf::ELF;
using google_breakpad::synth_elf::StringTable;
using google_breakpad::synth_elf::SymbolTable;
using google_breakpad::test_assembler::kLittleEndian;
using google_breakpad::test_assembler::Section;
using google_breakpad::WriteSymbolFileInternal;
using std::string;
using std::stringstream;
using std::vector;
using ::testing::Test;
@@ -67,7 +67,7 @@ class DumpSymbols : public Test {
void GetElfContents(ELF& elf) {
string contents;
ASSERT_TRUE(elf.GetContents(&contents));
ASSERT_LT(0, contents.size());
ASSERT_LT(0U, contents.size());
elfdata_v.clear();
elfdata_v.insert(elfdata_v.begin(), contents.begin(), contents.end());
@@ -81,17 +81,15 @@ class DumpSymbols : public Test {
TEST_F(DumpSymbols, Invalid) {
Elf32_Ehdr header;
memset(&header, 0, sizeof(header));
stringstream s;
EXPECT_FALSE(WriteSymbolFileInternal(reinterpret_cast<uint8_t*>(&header),
"foo",
"",
true,
s));
Module* module;
DumpOptions options(ALL_SYMBOL_DATA, true);
EXPECT_FALSE(ReadSymbolDataInternal(reinterpret_cast<uint8_t*>(&header),
"foo",
vector<string>(),
options,
&module));
}
// TODO(ted): Fix the dump_symbols code to deal with cross-word-size
// ELF files.
#if __ELF_NATIVE_CLASS == 32
TEST_F(DumpSymbols, SimplePublic32) {
ELF elf(EM_386, ELFCLASS32, kLittleEndian);
// Zero out text section for simplicity.
@@ -116,19 +114,22 @@ TEST_F(DumpSymbols, SimplePublic32) {
elf.Finish();
GetElfContents(elf);
Module* module;
DumpOptions options(ALL_SYMBOL_DATA, true);
EXPECT_TRUE(ReadSymbolDataInternal(elfdata,
"foo",
vector<string>(),
options,
&module));
stringstream s;
ASSERT_TRUE(WriteSymbolFileInternal(elfdata,
"foo",
"",
true,
s));
module->Write(s, ALL_SYMBOL_DATA);
EXPECT_EQ("MODULE Linux x86 000000000000000000000000000000000 foo\n"
"PUBLIC 1000 0 superfunc\n",
s.str());
delete module;
}
#endif
#if __ELF_NATIVE_CLASS == 64
TEST_F(DumpSymbols, SimplePublic64) {
ELF elf(EM_X86_64, ELFCLASS64, kLittleEndian);
// Zero out text section for simplicity.
@@ -153,14 +154,19 @@ TEST_F(DumpSymbols, SimplePublic64) {
elf.Finish();
GetElfContents(elf);
Module* module;
DumpOptions options(ALL_SYMBOL_DATA, true);
EXPECT_TRUE(ReadSymbolDataInternal(elfdata,
"foo",
vector<string>(),
options,
&module));
stringstream s;
ASSERT_TRUE(WriteSymbolFileInternal(elfdata,
"foo",
"",
true,
s));
module->Write(s, ALL_SYMBOL_DATA);
EXPECT_EQ("MODULE Linux x86_64 000000000000000000000000000000000 foo\n"
"PUBLIC 1000 0 superfunc\n",
s.str());
}
#endif
} // namespace google_breakpad

View File

@@ -37,11 +37,22 @@
//
#define HANDLE_EINTR(x) ({ \
typeof(x) __eintr_result__; \
typeof(x) eintr_wrapper_result; \
do { \
__eintr_result__ = x; \
} while (__eintr_result__ == -1 && errno == EINTR); \
__eintr_result__;\
eintr_wrapper_result = (x); \
} while (eintr_wrapper_result == -1 && errno == EINTR); \
eintr_wrapper_result; \
})
#endif // ifndef COMMON_LINUX_EINTR_WRAPPER_H_
#define IGNORE_EINTR(x) ({ \
typeof(x) eintr_wrapper_result; \
do { \
eintr_wrapper_result = (x); \
if (eintr_wrapper_result == -1 && errno == EINTR) { \
eintr_wrapper_result = 0; \
} \
} while (0); \
eintr_wrapper_result; \
})
#endif // COMMON_LINUX_EINTR_WRAPPER_H_

View File

@@ -34,15 +34,10 @@
#define COMMON_LINUX_ELF_CORE_DUMP_H_
#include <elf.h>
#if !defined(__ANDROID__)
#include <link.h>
#endif
#include <stddef.h>
#include "common/memory_range.h"
#if defined(__ANDROID__)
#include "common/linux/android_link.h"
#endif
namespace google_breakpad {

View File

@@ -39,6 +39,7 @@
#include "common/linux/memory_mapped_file.h"
#include "common/tests/file_utils.h"
#include "common/linux/tests/crash_generator.h"
#include "common/using_std_string.h"
using google_breakpad::AutoTempDir;
using google_breakpad::CrashGenerator;
@@ -47,13 +48,12 @@ using google_breakpad::MemoryMappedFile;
using google_breakpad::MemoryRange;
using google_breakpad::WriteFile;
using std::set;
using std::string;
TEST(ElfCoreDumpTest, DefaultConstructor) {
ElfCoreDump core;
EXPECT_FALSE(core.IsValid());
EXPECT_EQ(NULL, core.GetHeader());
EXPECT_EQ(0, core.GetProgramHeaderCount());
EXPECT_EQ(0U, core.GetProgramHeaderCount());
EXPECT_EQ(NULL, core.GetProgramHeader(0));
EXPECT_EQ(NULL, core.GetFirstProgramHeaderOfType(PT_LOAD));
EXPECT_FALSE(core.GetFirstNote().IsValid());
@@ -74,7 +74,7 @@ TEST(ElfCoreDumpTest, TestElfHeader) {
core.SetContent(mapped_core_file.content());
EXPECT_FALSE(core.IsValid());
EXPECT_EQ(NULL, core.GetHeader());
EXPECT_EQ(0, core.GetProgramHeaderCount());
EXPECT_EQ(0U, core.GetProgramHeaderCount());
EXPECT_EQ(NULL, core.GetProgramHeader(0));
EXPECT_EQ(NULL, core.GetFirstProgramHeaderOfType(PT_LOAD));
EXPECT_FALSE(core.GetFirstNote().IsValid());
@@ -182,8 +182,12 @@ TEST(ElfCoreDumpTest, ValidCoreFile) {
size_t num_nt_prpsinfo = 0;
size_t num_nt_prstatus = 0;
#if defined(__i386__) || defined(__x86_64__)
size_t num_nt_fpregset = 0;
#endif
#if defined(__i386__)
size_t num_nt_prxfpreg = 0;
#endif
set<pid_t> actual_thread_ids;
ElfCoreDump::Note note = core.GetFirstNote();
while (note.IsValid()) {
@@ -211,7 +215,7 @@ TEST(ElfCoreDumpTest, ValidCoreFile) {
++num_nt_prstatus;
break;
}
#if defined(__i386) || defined(__x86_64)
#if defined(__i386__) || defined(__x86_64__)
case NT_FPREGSET: {
EXPECT_TRUE(description.data() != NULL);
EXPECT_EQ(sizeof(user_fpregs_struct), description.length());
@@ -219,7 +223,7 @@ TEST(ElfCoreDumpTest, ValidCoreFile) {
break;
}
#endif
#if defined(__i386)
#if defined(__i386__)
case NT_PRXFPREG: {
EXPECT_TRUE(description.data() != NULL);
EXPECT_EQ(sizeof(user_fpxregs_struct), description.length());
@@ -234,12 +238,12 @@ TEST(ElfCoreDumpTest, ValidCoreFile) {
}
EXPECT_TRUE(expected_thread_ids == actual_thread_ids);
EXPECT_EQ(1, num_nt_prpsinfo);
EXPECT_EQ(1U, num_nt_prpsinfo);
EXPECT_EQ(kNumOfThreads, num_nt_prstatus);
#if defined(__i386) || defined(__x86_64)
#if defined(__i386__) || defined(__x86_64__)
EXPECT_EQ(kNumOfThreads, num_nt_fpregset);
#endif
#if defined(__i386)
#if defined(__i386__)
EXPECT_EQ(kNumOfThreads, num_nt_prxfpreg);
#endif
}

View File

@@ -0,0 +1,46 @@
// -*- mode: C++ -*-
// Copyright (c) 2013, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Original author: Lei Zhang <thestig@google.com>
// elf_gnu_compat.h: #defines unique to glibc's elf.h.
#ifndef COMMON_LINUX_ELF_GNU_COMPAT_H_
#define COMMON_LINUX_ELF_GNU_COMPAT_H_
#include <elf.h>
// A note type on GNU systems corresponding to the .note.gnu.build-id section.
#ifndef NT_GNU_BUILD_ID
#define NT_GNU_BUILD_ID 3
#endif
#endif // COMMON_LINUX_ELF_GNU_COMPAT_H_

View File

@@ -42,6 +42,7 @@
#include "common/linux/synth_elf.h"
#include "common/module.h"
#include "common/test_assembler.h"
#include "common/using_std_string.h"
using google_breakpad::Module;
using google_breakpad::synth_elf::StringTable;
@@ -52,7 +53,6 @@ using google_breakpad::test_assembler::Label;
using google_breakpad::test_assembler::Section;
using ::testing::Test;
using ::testing::TestWithParam;
using std::string;
using std::vector;
class ELFSymbolsToModuleTestFixture {

View File

@@ -0,0 +1,74 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef COMMON_LINUX_ELFUTILS_INL_H__
#define COMMON_LINUX_ELFUTILS_INL_H__
#include "common/linux/linux_libc_support.h"
#include "elfutils.h"
namespace google_breakpad {
template<typename ElfClass, typename T>
const T* GetOffset(const typename ElfClass::Ehdr* elf_header,
typename ElfClass::Off offset) {
return reinterpret_cast<const T*>(reinterpret_cast<uintptr_t>(elf_header) +
offset);
}
template<typename ElfClass>
const typename ElfClass::Shdr* FindElfSectionByName(
const char* name,
typename ElfClass::Word section_type,
const typename ElfClass::Shdr* sections,
const char* section_names,
const char* names_end,
int nsection) {
assert(name != NULL);
assert(sections != NULL);
assert(nsection > 0);
int name_len = my_strlen(name);
if (name_len == 0)
return NULL;
for (int i = 0; i < nsection; ++i) {
const char* section_name = section_names + sections[i].sh_name;
if (sections[i].sh_type == section_type &&
names_end - section_name >= name_len + 1 &&
my_strcmp(name, section_name) == 0) {
return sections + i;
}
}
return NULL;
}
} // namespace google_breakpad
#endif // COMMON_LINUX_ELFUTILS_INL_H__

View File

@@ -0,0 +1,194 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "common/linux/elfutils.h"
#include <assert.h>
#include <string.h>
#include "common/linux/linux_libc_support.h"
#include "common/linux/elfutils-inl.h"
namespace google_breakpad {
namespace {
template<typename ElfClass>
void FindElfClassSection(const char *elf_base,
const char *section_name,
typename ElfClass::Word section_type,
const void **section_start,
int *section_size) {
typedef typename ElfClass::Ehdr Ehdr;
typedef typename ElfClass::Shdr Shdr;
assert(elf_base);
assert(section_start);
assert(section_size);
assert(my_strncmp(elf_base, ELFMAG, SELFMAG) == 0);
const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
const Shdr* sections =
GetOffset<ElfClass,Shdr>(elf_header, elf_header->e_shoff);
const Shdr* section_names = sections + elf_header->e_shstrndx;
const char* names =
GetOffset<ElfClass,char>(elf_header, section_names->sh_offset);
const char *names_end = names + section_names->sh_size;
const Shdr* section =
FindElfSectionByName<ElfClass>(section_name, section_type,
sections, names, names_end,
elf_header->e_shnum);
if (section != NULL && section->sh_size > 0) {
*section_start = elf_base + section->sh_offset;
*section_size = section->sh_size;
}
}
template<typename ElfClass>
void FindElfClassSegment(const char *elf_base,
typename ElfClass::Word segment_type,
const void **segment_start,
int *segment_size) {
typedef typename ElfClass::Ehdr Ehdr;
typedef typename ElfClass::Phdr Phdr;
assert(elf_base);
assert(segment_start);
assert(segment_size);
assert(my_strncmp(elf_base, ELFMAG, SELFMAG) == 0);
const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
const Phdr* phdrs =
GetOffset<ElfClass,Phdr>(elf_header, elf_header->e_phoff);
for (int i = 0; i < elf_header->e_phnum; ++i) {
if (phdrs[i].p_type == segment_type) {
*segment_start = elf_base + phdrs[i].p_offset;
*segment_size = phdrs[i].p_filesz;
return;
}
}
}
} // namespace
bool IsValidElf(const void* elf_base) {
return my_strncmp(reinterpret_cast<const char*>(elf_base),
ELFMAG, SELFMAG) == 0;
}
int ElfClass(const void* elf_base) {
const ElfW(Ehdr)* elf_header =
reinterpret_cast<const ElfW(Ehdr)*>(elf_base);
return elf_header->e_ident[EI_CLASS];
}
bool FindElfSection(const void *elf_mapped_base,
const char *section_name,
uint32_t section_type,
const void **section_start,
int *section_size,
int *elfclass) {
assert(elf_mapped_base);
assert(section_start);
assert(section_size);
*section_start = NULL;
*section_size = 0;
if (!IsValidElf(elf_mapped_base))
return false;
int cls = ElfClass(elf_mapped_base);
if (elfclass) {
*elfclass = cls;
}
const char* elf_base =
static_cast<const char*>(elf_mapped_base);
if (cls == ELFCLASS32) {
FindElfClassSection<ElfClass32>(elf_base, section_name, section_type,
section_start, section_size);
return *section_start != NULL;
} else if (cls == ELFCLASS64) {
FindElfClassSection<ElfClass64>(elf_base, section_name, section_type,
section_start, section_size);
return *section_start != NULL;
}
return false;
}
bool FindElfSegment(const void *elf_mapped_base,
uint32_t segment_type,
const void **segment_start,
int *segment_size,
int *elfclass) {
assert(elf_mapped_base);
assert(segment_start);
assert(segment_size);
*segment_start = NULL;
*segment_size = 0;
if (!IsValidElf(elf_mapped_base))
return false;
int cls = ElfClass(elf_mapped_base);
if (elfclass) {
*elfclass = cls;
}
const char* elf_base =
static_cast<const char*>(elf_mapped_base);
if (cls == ELFCLASS32) {
FindElfClassSegment<ElfClass32>(elf_base, segment_type,
segment_start, segment_size);
return *segment_start != NULL;
} else if (cls == ELFCLASS64) {
FindElfClassSegment<ElfClass64>(elf_base, segment_type,
segment_start, segment_size);
return *segment_start != NULL;
}
return false;
}
} // namespace google_breakpad

View File

@@ -0,0 +1,118 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// elfutils.h: Utilities for dealing with ELF files.
//
#ifndef COMMON_LINUX_ELFUTILS_H__
#define COMMON_LINUX_ELFUTILS_H__
#include <elf.h>
#include <link.h>
#include <stdint.h>
namespace google_breakpad {
// Traits classes so consumers can write templatized code to deal
// with specific ELF bits.
struct ElfClass32 {
typedef Elf32_Addr Addr;
typedef Elf32_Ehdr Ehdr;
typedef Elf32_Nhdr Nhdr;
typedef Elf32_Phdr Phdr;
typedef Elf32_Shdr Shdr;
typedef Elf32_Half Half;
typedef Elf32_Off Off;
typedef Elf32_Word Word;
static const int kClass = ELFCLASS32;
static const size_t kAddrSize = sizeof(Elf32_Addr);
};
struct ElfClass64 {
typedef Elf64_Addr Addr;
typedef Elf64_Ehdr Ehdr;
typedef Elf64_Nhdr Nhdr;
typedef Elf64_Phdr Phdr;
typedef Elf64_Shdr Shdr;
typedef Elf64_Half Half;
typedef Elf64_Off Off;
typedef Elf64_Word Word;
static const int kClass = ELFCLASS64;
static const size_t kAddrSize = sizeof(Elf64_Addr);
};
bool IsValidElf(const void* elf_header);
int ElfClass(const void* elf_base);
// Attempt to find a section named |section_name| of type |section_type|
// in the ELF binary data at |elf_mapped_base|. On success, returns true
// and sets |*section_start| to point to the start of the section data,
// and |*section_size| to the size of the section's data. If |elfclass|
// is not NULL, set |*elfclass| to the ELF file class.
bool FindElfSection(const void *elf_mapped_base,
const char *section_name,
uint32_t section_type,
const void **section_start,
int *section_size,
int *elfclass);
// Internal helper method, exposed for convenience for callers
// that already have more info.
template<typename ElfClass>
const typename ElfClass::Shdr*
FindElfSectionByName(const char* name,
typename ElfClass::Word section_type,
const typename ElfClass::Shdr* sections,
const char* section_names,
const char* names_end,
int nsection);
// Attempt to find the first segment of type |segment_type| in the ELF
// binary data at |elf_mapped_base|. On success, returns true and sets
// |*segment_start| to point to the start of the segment data, and
// and |*segment_size| to the size of the segment's data. If |elfclass|
// is not NULL, set |*elfclass| to the ELF file class.
bool FindElfSegment(const void *elf_mapped_base,
uint32_t segment_type,
const void **segment_start,
int *segment_size,
int *elfclass);
// Convert an offset from an Elf header into a pointer to the mapped
// address in the current process. Takes an extra template parameter
// to specify the return type to avoid having to dynamic_cast the
// result.
template<typename ElfClass, typename T>
const T*
GetOffset(const typename ElfClass::Ehdr* elf_header,
typename ElfClass::Off offset);
} // namespace google_breakpad
#endif // COMMON_LINUX_ELFUTILS_H__

View File

@@ -36,144 +36,50 @@
#include <arpa/inet.h>
#include <assert.h>
#if defined(__ANDROID__)
#include <linux/elf.h>
#include "client/linux/android_link.h"
#else
#include <elf.h>
#include <link.h>
#endif
#include <string.h>
#include <algorithm>
#include "common/linux/elf_gnu_compat.h"
#include "common/linux/elfutils.h"
#include "common/linux/linux_libc_support.h"
#include "common/linux/memory_mapped_file.h"
#include "third_party/lss/linux_syscall_support.h"
namespace google_breakpad {
#ifndef NT_GNU_BUILD_ID
#define NT_GNU_BUILD_ID 3
#endif
FileID::FileID(const char* path) {
strncpy(path_, path, sizeof(path_));
}
struct ElfClass32 {
typedef Elf32_Ehdr Ehdr;
typedef Elf32_Nhdr Nhdr;
typedef Elf32_Shdr Shdr;
static const int kClass = ELFCLASS32;
};
// ELF note name and desc are 32-bits word padded.
#define NOTE_PADDING(a) ((a + 3) & ~3)
struct ElfClass64 {
typedef Elf64_Ehdr Ehdr;
typedef Elf64_Nhdr Nhdr;
typedef Elf64_Shdr Shdr;
static const int kClass = ELFCLASS64;
};
// These six functions are also used inside the crashed process, so be safe
// These functions are also used inside the crashed process, so be safe
// and use the syscall/libc wrappers instead of direct syscalls or libc.
template<typename ElfClass>
static void FindElfClassSection(const char *elf_base,
const char *section_name,
uint32_t section_type,
const void **section_start,
int *section_size) {
typedef typename ElfClass::Ehdr Ehdr;
typedef typename ElfClass::Shdr Shdr;
assert(elf_base);
assert(section_start);
assert(section_size);
assert(my_strncmp(elf_base, ELFMAG, SELFMAG) == 0);
int name_len = my_strlen(section_name);
const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
const Shdr* sections =
reinterpret_cast<const Shdr*>(elf_base + elf_header->e_shoff);
const Shdr* string_section = sections + elf_header->e_shstrndx;
const Shdr* section = NULL;
for (int i = 0; i < elf_header->e_shnum; ++i) {
if (sections[i].sh_type == section_type) {
const char* current_section_name = (char*)(elf_base +
string_section->sh_offset +
sections[i].sh_name);
if (!my_strncmp(current_section_name, section_name, name_len)) {
section = &sections[i];
break;
}
}
}
if (section != NULL && section->sh_size > 0) {
*section_start = elf_base + section->sh_offset;
*section_size = section->sh_size;
}
}
// Attempt to find a section named |section_name| of type |section_type|
// in the ELF binary data at |elf_mapped_base|. On success, returns true
// and sets |*section_start| to point to the start of the section data,
// and |*section_size| to the size of the section's data. If |elfclass|
// is not NULL, set |*elfclass| to the ELF file class.
static bool FindElfSection(const void *elf_mapped_base,
const char *section_name,
uint32_t section_type,
const void **section_start,
int *section_size,
int *elfclass) {
assert(elf_mapped_base);
assert(section_start);
assert(section_size);
*section_start = NULL;
*section_size = 0;
const char* elf_base =
static_cast<const char*>(elf_mapped_base);
const ElfW(Ehdr)* elf_header =
reinterpret_cast<const ElfW(Ehdr)*>(elf_base);
if (my_strncmp(elf_base, ELFMAG, SELFMAG) != 0)
return false;
if (elfclass) {
*elfclass = elf_header->e_ident[EI_CLASS];
}
if (elf_header->e_ident[EI_CLASS] == ELFCLASS32) {
FindElfClassSection<ElfClass32>(elf_base, section_name, section_type,
section_start, section_size);
return *section_start != NULL;
} else if (elf_header->e_ident[EI_CLASS] == ELFCLASS64) {
FindElfClassSection<ElfClass64>(elf_base, section_name, section_type,
section_start, section_size);
return *section_start != NULL;
}
return false;
}
template<typename ElfClass>
static bool ElfClassBuildIDNoteIdentifier(const void *section,
static bool ElfClassBuildIDNoteIdentifier(const void *section, int length,
uint8_t identifier[kMDGUIDSize]) {
typedef typename ElfClass::Nhdr Nhdr;
const void* section_end = reinterpret_cast<const char*>(section) + length;
const Nhdr* note_header = reinterpret_cast<const Nhdr*>(section);
if (note_header->n_type != NT_GNU_BUILD_ID ||
while (reinterpret_cast<const void *>(note_header) < section_end) {
if (note_header->n_type == NT_GNU_BUILD_ID)
break;
note_header = reinterpret_cast<const Nhdr*>(
reinterpret_cast<const char*>(note_header) + sizeof(Nhdr) +
NOTE_PADDING(note_header->n_namesz) +
NOTE_PADDING(note_header->n_descsz));
}
if (reinterpret_cast<const void *>(note_header) >= section_end ||
note_header->n_descsz == 0) {
return false;
}
const char* build_id = reinterpret_cast<const char*>(section) +
sizeof(Nhdr) + note_header->n_namesz;
const char* build_id = reinterpret_cast<const char*>(note_header) +
sizeof(Nhdr) + NOTE_PADDING(note_header->n_namesz);
// Copy as many bits of the build ID as will fit
// into the GUID space.
my_memset(identifier, 0, kMDGUIDSize);
@@ -189,16 +95,21 @@ static bool FindElfBuildIDNote(const void *elf_mapped_base,
uint8_t identifier[kMDGUIDSize]) {
void* note_section;
int note_size, elfclass;
if (!FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE,
(const void**)&note_section, &note_size, &elfclass) ||
note_size == 0) {
if ((!FindElfSegment(elf_mapped_base, PT_NOTE,
(const void**)&note_section, &note_size, &elfclass) ||
note_size == 0) &&
(!FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE,
(const void**)&note_section, &note_size, &elfclass) ||
note_size == 0)) {
return false;
}
if (elfclass == ELFCLASS32) {
return ElfClassBuildIDNoteIdentifier<ElfClass32>(note_section, identifier);
return ElfClassBuildIDNoteIdentifier<ElfClass32>(note_section, note_size,
identifier);
} else if (elfclass == ELFCLASS64) {
return ElfClassBuildIDNoteIdentifier<ElfClass64>(note_section, identifier);
return ElfClassBuildIDNoteIdentifier<ElfClass64>(note_section, note_size,
identifier);
}
return false;

View File

@@ -32,19 +32,27 @@
#include <elf.h>
#include <stdlib.h>
#include <string>
#include "common/linux/elf_gnu_compat.h"
#include "common/linux/elfutils.h"
#include "common/linux/file_id.h"
#include "common/linux/safe_readlink.h"
#include "common/linux/synth_elf.h"
#include "common/test_assembler.h"
#include "common/tests/auto_tempdir.h"
#include "common/using_std_string.h"
#include "breakpad_googletest_includes.h"
using namespace google_breakpad;
using google_breakpad::ElfClass32;
using google_breakpad::ElfClass64;
using google_breakpad::SafeReadLink;
using google_breakpad::synth_elf::BuildIDNote;
using google_breakpad::synth_elf::ELF;
using google_breakpad::synth_elf::Notes;
using google_breakpad::test_assembler::kLittleEndian;
using google_breakpad::test_assembler::Section;
using ::testing::Types;
namespace {
@@ -58,6 +66,9 @@ void PopulateSection(Section* section, int size, int prime_number) {
} // namespace
#ifndef __ANDROID__
// This test is disabled on Android: It will always fail, since there is no
// 'strip' binary installed on test devices.
TEST(FileIDStripTest, StripSelf) {
// Calculate the File ID of this binary using
// FileID::ElfFileIdentifier, then make a copy of this binary,
@@ -67,12 +78,14 @@ TEST(FileIDStripTest, StripSelf) {
// copy our binary to a temp file, and strip it
AutoTempDir temp_dir;
std::string templ = temp_dir.path() + "/file-id-unittest";
string templ = temp_dir.path() + "/file-id-unittest";
char cmdline[4096];
sprintf(cmdline, "cp \"%s\" \"%s\"", exe_name, templ.c_str());
ASSERT_EQ(system(cmdline), 0);
ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline;
sprintf(cmdline, "chmod u+w \"%s\"", templ.c_str());
ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline;
sprintf(cmdline, "strip \"%s\"", templ.c_str());
ASSERT_EQ(system(cmdline), 0);
ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline;
uint8_t identifier1[sizeof(MDGUID)];
uint8_t identifier2[sizeof(MDGUID)];
@@ -88,13 +101,15 @@ TEST(FileIDStripTest, StripSelf) {
37);
EXPECT_STREQ(identifier_string1, identifier_string2);
}
#endif // !__ANDROID__
template<typename ElfClass>
class FileIDTest : public testing::Test {
public:
void GetElfContents(ELF& elf) {
string contents;
ASSERT_TRUE(elf.GetContents(&contents));
ASSERT_LT(0, contents.size());
ASSERT_LT(0U, contents.size());
elfdata_v.clear();
elfdata_v.insert(elfdata_v.begin(), contents.begin(), contents.end());
@@ -105,48 +120,35 @@ public:
uint8_t* elfdata;
};
TEST_F(FileIDTest, ElfClass) {
typedef Types<ElfClass32, ElfClass64> ElfClasses;
TYPED_TEST_CASE(FileIDTest, ElfClasses);
TYPED_TEST(FileIDTest, ElfClass) {
uint8_t identifier[sizeof(MDGUID)];
const char expected_identifier_string[] =
"80808080-8080-0000-0000-008080808080";
char identifier_string[sizeof(expected_identifier_string)];
const size_t kTextSectionSize = 128;
ELF elf32(EM_386, ELFCLASS32, kLittleEndian);
Section text32(kLittleEndian);
ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
Section text(kLittleEndian);
for (size_t i = 0; i < kTextSectionSize; ++i) {
text32.D8(i * 3);
text.D8(i * 3);
}
elf32.AddSection(".text", text32, SHT_PROGBITS);
elf32.Finish();
GetElfContents(elf32);
elf.AddSection(".text", text, SHT_PROGBITS);
elf.Finish();
this->GetElfContents(elf);
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier));
FileID::ConvertIdentifierToString(identifier, identifier_string,
sizeof(identifier_string));
EXPECT_STREQ(expected_identifier_string, identifier_string);
memset(identifier, 0, sizeof(identifier));
memset(identifier_string, 0, sizeof(identifier_string));
ELF elf64(EM_X86_64, ELFCLASS64, kLittleEndian);
Section text64(kLittleEndian);
for (size_t i = 0; i < kTextSectionSize; ++i) {
text64.D8(i * 3);
}
elf64.AddSection(".text", text64, SHT_PROGBITS);
elf64.Finish();
GetElfContents(elf64);
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier));
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
identifier));
FileID::ConvertIdentifierToString(identifier, identifier_string,
sizeof(identifier_string));
EXPECT_STREQ(expected_identifier_string, identifier_string);
}
TEST_F(FileIDTest, BuildID) {
TYPED_TEST(FileIDTest, BuildID) {
const uint8_t kExpectedIdentifier[sizeof(MDGUID)] =
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
@@ -159,35 +161,54 @@ TEST_F(FileIDTest, BuildID) {
uint8_t identifier[sizeof(MDGUID)];
char identifier_string[sizeof(expected_identifier_string)];
ELF elf32(EM_386, ELFCLASS32, kLittleEndian);
ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
Section text(kLittleEndian);
text.Append(4096, 0);
elf32.AddSection(".text", text, SHT_PROGBITS);
BuildIDNote::AppendSection(elf32,
kExpectedIdentifier,
sizeof(kExpectedIdentifier));
elf32.Finish();
GetElfContents(elf32);
elf.AddSection(".text", text, SHT_PROGBITS);
Notes notes(kLittleEndian);
notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifier,
sizeof(kExpectedIdentifier));
elf.AddSection(".note.gnu.build-id", notes, SHT_NOTE);
elf.Finish();
this->GetElfContents(elf);
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier));
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
identifier));
FileID::ConvertIdentifierToString(identifier, identifier_string,
sizeof(identifier_string));
EXPECT_STREQ(expected_identifier_string, identifier_string);
}
memset(identifier, 0, sizeof(identifier));
memset(identifier_string, 0, sizeof(identifier_string));
TYPED_TEST(FileIDTest, BuildIDPH) {
const uint8_t kExpectedIdentifier[sizeof(MDGUID)] =
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
char expected_identifier_string[] =
"00000000-0000-0000-0000-000000000000";
FileID::ConvertIdentifierToString(kExpectedIdentifier,
expected_identifier_string,
sizeof(expected_identifier_string));
ELF elf64(EM_X86_64, ELFCLASS64, kLittleEndian);
// Re-use empty text section from previous test
elf64.AddSection(".text", text, SHT_PROGBITS);
BuildIDNote::AppendSection(elf64,
kExpectedIdentifier,
sizeof(kExpectedIdentifier));
elf64.Finish();
GetElfContents(elf64);
uint8_t identifier[sizeof(MDGUID)];
char identifier_string[sizeof(expected_identifier_string)];
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier));
ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
Section text(kLittleEndian);
text.Append(4096, 0);
elf.AddSection(".text", text, SHT_PROGBITS);
Notes notes(kLittleEndian);
notes.AddNote(0, "Linux",
reinterpret_cast<const uint8_t *>("\0x42\0x02\0\0"), 4);
notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifier,
sizeof(kExpectedIdentifier));
int note_idx = elf.AddSection(".note", notes, SHT_NOTE);
elf.AddSegment(note_idx, note_idx, PT_NOTE);
elf.Finish();
this->GetElfContents(elf);
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
identifier));
FileID::ConvertIdentifierToString(identifier, identifier_string,
sizeof(identifier_string));
@@ -196,7 +217,7 @@ TEST_F(FileIDTest, BuildID) {
// Test to make sure two files with different text sections produce
// different hashes when not using a build id.
TEST_F(FileIDTest, UniqueHashes32) {
TYPED_TEST(FileIDTest, UniqueHashes) {
char identifier_string_1[] =
"00000000-0000-0000-0000-000000000000";
char identifier_string_2[] =
@@ -205,7 +226,7 @@ TEST_F(FileIDTest, UniqueHashes32) {
uint8_t identifier_2[sizeof(MDGUID)];
{
ELF elf1(EM_386, ELFCLASS32, kLittleEndian);
ELF elf1(EM_386, TypeParam::kClass, kLittleEndian);
Section foo_1(kLittleEndian);
PopulateSection(&foo_1, 32, 5);
elf1.AddSection(".foo", foo_1, SHT_PROGBITS);
@@ -213,15 +234,16 @@ TEST_F(FileIDTest, UniqueHashes32) {
PopulateSection(&text_1, 4096, 17);
elf1.AddSection(".text", text_1, SHT_PROGBITS);
elf1.Finish();
GetElfContents(elf1);
this->GetElfContents(elf1);
}
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier_1));
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
identifier_1));
FileID::ConvertIdentifierToString(identifier_1, identifier_string_1,
sizeof(identifier_string_1));
{
ELF elf2(EM_386, ELFCLASS32, kLittleEndian);
ELF elf2(EM_386, TypeParam::kClass, kLittleEndian);
Section text_2(kLittleEndian);
Section foo_2(kLittleEndian);
PopulateSection(&foo_2, 32, 5);
@@ -229,54 +251,11 @@ TEST_F(FileIDTest, UniqueHashes32) {
PopulateSection(&text_2, 4096, 31);
elf2.AddSection(".text", text_2, SHT_PROGBITS);
elf2.Finish();
GetElfContents(elf2);
this->GetElfContents(elf2);
}
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier_2));
FileID::ConvertIdentifierToString(identifier_2, identifier_string_2,
sizeof(identifier_string_2));
EXPECT_STRNE(identifier_string_1, identifier_string_2);
}
// Same as UniqueHashes32, for x86-64.
TEST_F(FileIDTest, UniqueHashes64) {
char identifier_string_1[] =
"00000000-0000-0000-0000-000000000000";
char identifier_string_2[] =
"00000000-0000-0000-0000-000000000000";
uint8_t identifier_1[sizeof(MDGUID)];
uint8_t identifier_2[sizeof(MDGUID)];
{
ELF elf1(EM_X86_64, ELFCLASS64, kLittleEndian);
Section foo_1(kLittleEndian);
PopulateSection(&foo_1, 32, 5);
elf1.AddSection(".foo", foo_1, SHT_PROGBITS);
Section text_1(kLittleEndian);
PopulateSection(&text_1, 4096, 17);
elf1.AddSection(".text", text_1, SHT_PROGBITS);
elf1.Finish();
GetElfContents(elf1);
}
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier_1));
FileID::ConvertIdentifierToString(identifier_1, identifier_string_1,
sizeof(identifier_string_1));
{
ELF elf2(EM_X86_64, ELFCLASS64, kLittleEndian);
Section text_2(kLittleEndian);
Section foo_2(kLittleEndian);
PopulateSection(&foo_2, 32, 5);
elf2.AddSection(".foo", foo_2, SHT_PROGBITS);
PopulateSection(&text_2, 4096, 31);
elf2.AddSection(".text", text_2, SHT_PROGBITS);
elf2.Finish();
GetElfContents(elf2);
}
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier_2));
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
identifier_2));
FileID::ConvertIdentifierToString(identifier_2, identifier_string_2,
sizeof(identifier_string_2));

View File

@@ -37,21 +37,21 @@
#include <iostream>
using std::string;
#include "common/using_std_string.h"
namespace google_breakpad {
GoogleCrashdumpUploader::GoogleCrashdumpUploader(const std::string& product,
const std::string& version,
const std::string& guid,
const std::string& ptime,
const std::string& ctime,
const std::string& email,
const std::string& comments,
const std::string& minidump_pathname,
const std::string& crash_server,
const std::string& proxy_host,
const std::string& proxy_userpassword) {
GoogleCrashdumpUploader::GoogleCrashdumpUploader(const string& product,
const string& version,
const string& guid,
const string& ptime,
const string& ctime,
const string& email,
const string& comments,
const string& minidump_pathname,
const string& crash_server,
const string& proxy_host,
const string& proxy_userpassword) {
LibcurlWrapper* http_layer = new LibcurlWrapper();
Init(product,
version,
@@ -67,17 +67,17 @@ GoogleCrashdumpUploader::GoogleCrashdumpUploader(const std::string& product,
http_layer);
}
GoogleCrashdumpUploader::GoogleCrashdumpUploader(const std::string& product,
const std::string& version,
const std::string& guid,
const std::string& ptime,
const std::string& ctime,
const std::string& email,
const std::string& comments,
const std::string& minidump_pathname,
const std::string& crash_server,
const std::string& proxy_host,
const std::string& proxy_userpassword,
GoogleCrashdumpUploader::GoogleCrashdumpUploader(const string& product,
const string& version,
const string& guid,
const string& ptime,
const string& ctime,
const string& email,
const string& comments,
const string& minidump_pathname,
const string& crash_server,
const string& proxy_host,
const string& proxy_userpassword,
LibcurlWrapper* http_layer) {
Init(product,
version,
@@ -93,17 +93,17 @@ GoogleCrashdumpUploader::GoogleCrashdumpUploader(const std::string& product,
http_layer);
}
void GoogleCrashdumpUploader::Init(const std::string& product,
const std::string& version,
const std::string& guid,
const std::string& ptime,
const std::string& ctime,
const std::string& email,
const std::string& comments,
const std::string& minidump_pathname,
const std::string& crash_server,
const std::string& proxy_host,
const std::string& proxy_userpassword,
void GoogleCrashdumpUploader::Init(const string& product,
const string& version,
const string& guid,
const string& ptime,
const string& ctime,
const string& email,
const string& comments,
const string& minidump_pathname,
const string& crash_server,
const string& proxy_host,
const string& proxy_userpassword,
LibcurlWrapper* http_layer) {
product_ = product;
version_ = version;
@@ -137,7 +137,7 @@ void GoogleCrashdumpUploader::Init(const std::string& product,
}
bool GoogleCrashdumpUploader::CheckRequiredParametersArePresent() {
std::string error_text;
string error_text;
if (product_.empty()) {
error_text.append("\nProduct name must be specified.");
}

View File

@@ -31,48 +31,50 @@
#include <string>
#include <map>
#include "common/using_std_string.h"
namespace google_breakpad {
class LibcurlWrapper;
class GoogleCrashdumpUploader {
public:
GoogleCrashdumpUploader(const std::string& product,
const std::string& version,
const std::string& guid,
const std::string& ptime,
const std::string& ctime,
const std::string& email,
const std::string& comments,
const std::string& minidump_pathname,
const std::string& crash_server,
const std::string& proxy_host,
const std::string& proxy_userpassword);
GoogleCrashdumpUploader(const string& product,
const string& version,
const string& guid,
const string& ptime,
const string& ctime,
const string& email,
const string& comments,
const string& minidump_pathname,
const string& crash_server,
const string& proxy_host,
const string& proxy_userpassword);
GoogleCrashdumpUploader(const std::string& product,
const std::string& version,
const std::string& guid,
const std::string& ptime,
const std::string& ctime,
const std::string& email,
const std::string& comments,
const std::string& minidump_pathname,
const std::string& crash_server,
const std::string& proxy_host,
const std::string& proxy_userpassword,
GoogleCrashdumpUploader(const string& product,
const string& version,
const string& guid,
const string& ptime,
const string& ctime,
const string& email,
const string& comments,
const string& minidump_pathname,
const string& crash_server,
const string& proxy_host,
const string& proxy_userpassword,
LibcurlWrapper* http_layer);
void Init(const std::string& product,
const std::string& version,
const std::string& guid,
const std::string& ptime,
const std::string& ctime,
const std::string& email,
const std::string& comments,
const std::string& minidump_pathname,
const std::string& crash_server,
const std::string& proxy_host,
const std::string& proxy_userpassword,
void Init(const string& product,
const string& version,
const string& guid,
const string& ptime,
const string& ctime,
const string& email,
const string& comments,
const string& minidump_pathname,
const string& crash_server,
const string& proxy_host,
const string& proxy_userpassword,
LibcurlWrapper* http_layer);
bool Upload();
@@ -80,19 +82,19 @@ class GoogleCrashdumpUploader {
bool CheckRequiredParametersArePresent();
LibcurlWrapper* http_layer_;
std::string product_;
std::string version_;
std::string guid_;
std::string ptime_;
std::string ctime_;
std::string email_;
std::string comments_;
std::string minidump_pathname_;
string product_;
string version_;
string guid_;
string ptime_;
string ctime_;
string email_;
string comments_;
string minidump_pathname_;
std::string crash_server_;
std::string proxy_host_;
std::string proxy_userpassword_;
string crash_server_;
string proxy_host_;
string proxy_userpassword_;
std::map<std::string, std::string> parameters_;
std::map<string, string> parameters_;
};
}

View File

@@ -29,9 +29,12 @@
// Unit test for crash dump uploader.
#include <string>
#include "common/linux/google_crashdump_uploader.h"
#include "common/linux/libcurl_wrapper.h"
#include "breakpad_googletest_includes.h"
#include "common/using_std_string.h"
namespace google_breakpad {
@@ -41,14 +44,14 @@ using ::testing::_;
class MockLibcurlWrapper : public LibcurlWrapper {
public:
MOCK_METHOD0(Init, bool());
MOCK_METHOD2(SetProxy, bool(const std::string& proxy_host,
const std::string& proxy_userpwd));
MOCK_METHOD2(AddFile, bool(const std::string& upload_file_path,
const std::string& basename));
MOCK_METHOD2(SetProxy, bool(const string& proxy_host,
const string& proxy_userpwd));
MOCK_METHOD2(AddFile, bool(const string& upload_file_path,
const string& basename));
MOCK_METHOD3(SendRequest,
bool(const std::string& url,
const std::map<std::string, std::string>& parameters,
std::string* server_response));
bool(const string& url,
const std::map<string, string>& parameters,
string* server_response));
};
class GoogleCrashdumpUploaderTest : public ::testing::Test {

View File

@@ -46,14 +46,14 @@
//
class GUIDGenerator {
public:
static u_int32_t BytesToUInt32(const u_int8_t bytes[]) {
return ((u_int32_t) bytes[0]
| ((u_int32_t) bytes[1] << 8)
| ((u_int32_t) bytes[2] << 16)
| ((u_int32_t) bytes[3] << 24));
static uint32_t BytesToUInt32(const uint8_t bytes[]) {
return ((uint32_t) bytes[0]
| ((uint32_t) bytes[1] << 8)
| ((uint32_t) bytes[2] << 16)
| ((uint32_t) bytes[3] << 24));
}
static void UInt32ToBytes(u_int8_t bytes[], u_int32_t n) {
static void UInt32ToBytes(uint8_t bytes[], uint32_t n) {
bytes[0] = n & 0xff;
bytes[1] = (n >> 8) & 0xff;
bytes[2] = (n >> 16) & 0xff;
@@ -63,8 +63,8 @@ class GUIDGenerator {
static bool CreateGUID(GUID *guid) {
InitOnce();
guid->data1 = random();
guid->data2 = (u_int16_t)(random());
guid->data3 = (u_int16_t)(random());
guid->data2 = (uint16_t)(random());
guid->data3 = (uint16_t)(random());
UInt32ToBytes(&guid->data4[0], random());
UInt32ToBytes(&guid->data4[4], random());
return true;

View File

@@ -0,0 +1,40 @@
// Copyright (c) 2012 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef COMMON_LINUX_IGNORE_RET_H_
#define COMMON_LINUX_IGNORE_RET_H_
// Some compilers are prone to warn about unused return values. In cases where
// either a) the call cannot fail, or b) there is nothing that can be done when
// the call fails, IGNORE_RET() can be used to mark the return code as ignored.
// This avoids spurious compiler warnings.
#define IGNORE_RET(x) do { if (x); } while (0)
#endif // COMMON_LINUX_IGNORE_RET_H_

View File

@@ -33,8 +33,7 @@
#include <string>
#include "common/linux/libcurl_wrapper.h"
using std::string;
#include "common/using_std_string.h"
namespace google_breakpad {
LibcurlWrapper::LibcurlWrapper()
@@ -58,8 +57,8 @@ LibcurlWrapper::LibcurlWrapper()
return;
}
bool LibcurlWrapper::SetProxy(const std::string& proxy_host,
const std::string& proxy_userpwd) {
bool LibcurlWrapper::SetProxy(const string& proxy_host,
const string& proxy_userpwd) {
if (!init_ok_) {
return false;
}
@@ -80,8 +79,8 @@ bool LibcurlWrapper::SetProxy(const std::string& proxy_host,
return true;
}
bool LibcurlWrapper::AddFile(const std::string& upload_file_path,
const std::string& basename) {
bool LibcurlWrapper::AddFile(const string& upload_file_path,
const string& basename) {
if (!init_ok_) {
return false;
}
@@ -101,17 +100,17 @@ static size_t WriteCallback(void *ptr, size_t size,
if (!userp)
return 0;
std::string *response = reinterpret_cast<std::string *>(userp);
string *response = reinterpret_cast<string *>(userp);
size_t real_size = size * nmemb;
response->append(reinterpret_cast<char *>(ptr), real_size);
return real_size;
}
bool LibcurlWrapper::SendRequest(const std::string& url,
const std::map<std::string, std::string>& parameters,
std::string* server_response) {
bool LibcurlWrapper::SendRequest(const string& url,
const std::map<string, string>& parameters,
string* server_response) {
(*easy_setopt_)(curl_, CURLOPT_URL, url.c_str());
std::map<std::string, std::string>::const_iterator iter = parameters.begin();
std::map<string, string>::const_iterator iter = parameters.begin();
for (; iter != parameters.end(); ++iter)
(*formadd_)(&formpost_, &lastptr_,
CURLFORM_COPYNAME, iter->first.c_str(),

View File

@@ -33,6 +33,7 @@
#include <string>
#include <map>
#include "common/using_std_string.h"
#include "third_party/curl/curl.h"
namespace google_breakpad {
@@ -40,13 +41,13 @@ class LibcurlWrapper {
public:
LibcurlWrapper();
virtual bool Init();
virtual bool SetProxy(const std::string& proxy_host,
const std::string& proxy_userpwd);
virtual bool AddFile(const std::string& upload_file_path,
const std::string& basename);
virtual bool SendRequest(const std::string& url,
const std::map<std::string, std::string>& parameters,
std::string* server_response);
virtual bool SetProxy(const string& proxy_host,
const string& proxy_userpwd);
virtual bool AddFile(const string& upload_file_path,
const string& basename);
virtual bool SendRequest(const string& url,
const std::map<string, string>& parameters,
string* server_response);
private:
// This function initializes class state corresponding to function
// pointers into the CURL library.
@@ -55,7 +56,7 @@ class LibcurlWrapper {
bool init_ok_; // Whether init succeeded
void* curl_lib_; // Pointer to result of dlopen() on
// curl library
std::string last_curl_error_; // The text of the last error when
string last_curl_error_; // The text of the last error when
// dealing
// with CURL.

View File

@@ -0,0 +1,237 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This source file provides replacements for libc functions that we need. If
// we call the libc functions directly we risk crashing in the dynamic linker
// as it tries to resolve uncached PLT entries.
#include "common/linux/linux_libc_support.h"
#include <stddef.h>
extern "C" {
size_t my_strlen(const char* s) {
size_t len = 0;
while (*s++) len++;
return len;
}
int my_strcmp(const char* a, const char* b) {
for (;;) {
if (*a < *b)
return -1;
else if (*a > *b)
return 1;
else if (*a == 0)
return 0;
a++;
b++;
}
}
int my_strncmp(const char* a, const char* b, size_t len) {
for (size_t i = 0; i < len; ++i) {
if (*a < *b)
return -1;
else if (*a > *b)
return 1;
else if (*a == 0)
return 0;
a++;
b++;
}
return 0;
}
// Parse a non-negative integer.
// result: (output) the resulting non-negative integer
// s: a NUL terminated string
// Return true iff successful.
bool my_strtoui(int* result, const char* s) {
if (*s == 0)
return false;
int r = 0;
for (;; s++) {
if (*s == 0)
break;
const int old_r = r;
r *= 10;
if (*s < '0' || *s > '9')
return false;
r += *s - '0';
if (r < old_r)
return false;
}
*result = r;
return true;
}
// Return the length of the given unsigned integer when expressed in base 10.
unsigned my_uint_len(uintmax_t i) {
if (!i)
return 1;
int len = 0;
while (i) {
len++;
i /= 10;
}
return len;
}
// Convert an unsigned integer to a string
// output: (output) the resulting string is written here. This buffer must be
// large enough to hold the resulting string. Call |my_uint_len| to get the
// required length.
// i: the unsigned integer to serialise.
// i_len: the length of the integer in base 10 (see |my_uint_len|).
void my_uitos(char* output, uintmax_t i, unsigned i_len) {
for (unsigned index = i_len; index; --index, i /= 10)
output[index - 1] = '0' + (i % 10);
}
const char* my_strchr(const char* haystack, char needle) {
while (*haystack && *haystack != needle)
haystack++;
if (*haystack == needle)
return haystack;
return (const char*) 0;
}
const char* my_strrchr(const char* haystack, char needle) {
const char* ret = NULL;
while (*haystack) {
if (*haystack == needle)
ret = haystack;
haystack++;
}
return ret;
}
void* my_memchr(const void* src, int needle, size_t src_len) {
const unsigned char* p = (const unsigned char*)src;
const unsigned char* p_end = p + src_len;
for (; p < p_end; ++p) {
if (*p == needle)
return (void*)p;
}
return NULL;
}
// Read a hex value
// result: (output) the resulting value
// s: a string
// Returns a pointer to the first invalid charactor.
const char* my_read_hex_ptr(uintptr_t* result, const char* s) {
uintptr_t r = 0;
for (;; ++s) {
if (*s >= '0' && *s <= '9') {
r <<= 4;
r += *s - '0';
} else if (*s >= 'a' && *s <= 'f') {
r <<= 4;
r += (*s - 'a') + 10;
} else if (*s >= 'A' && *s <= 'F') {
r <<= 4;
r += (*s - 'A') + 10;
} else {
break;
}
}
*result = r;
return s;
}
const char* my_read_decimal_ptr(uintptr_t* result, const char* s) {
uintptr_t r = 0;
for (;; ++s) {
if (*s >= '0' && *s <= '9') {
r *= 10;
r += *s - '0';
} else {
break;
}
}
*result = r;
return s;
}
void my_memset(void* ip, char c, size_t len) {
char* p = (char *) ip;
while (len--)
*p++ = c;
}
size_t my_strlcpy(char* s1, const char* s2, size_t len) {
size_t pos1 = 0;
size_t pos2 = 0;
while (s2[pos2] != '\0') {
if (pos1 + 1 < len) {
s1[pos1] = s2[pos2];
pos1++;
}
pos2++;
}
if (len > 0)
s1[pos1] = '\0';
return pos2;
}
size_t my_strlcat(char* s1, const char* s2, size_t len) {
size_t pos1 = 0;
while (pos1 < len && s1[pos1] != '\0')
pos1++;
if (pos1 == len)
return pos1;
return pos1 + my_strlcpy(s1 + pos1, s2, len - pos1);
}
int my_isspace(int ch) {
// Matches the C locale.
const char spaces[] = " \t\f\n\r\t\v";
for (size_t i = 0; i < sizeof(spaces); i++) {
if (ch == spaces[i])
return 1;
}
return 0;
}
} // extern "C"

View File

@@ -40,138 +40,56 @@
extern "C" {
static inline size_t
my_strlen(const char* s) {
size_t len = 0;
while (*s++) len++;
return len;
}
extern size_t my_strlen(const char* s);
static inline int
my_strcmp(const char* a, const char* b) {
for (;;) {
if (*a < *b)
return -1;
else if (*a > *b)
return 1;
else if (*a == 0)
return 0;
a++;
b++;
}
}
extern int my_strcmp(const char* a, const char* b);
static inline int
my_strncmp(const char* a, const char* b, size_t len) {
for (size_t i = 0; i < len; ++i) {
if (*a < *b)
return -1;
else if (*a > *b)
return 1;
else if (*a == 0)
return 0;
a++;
b++;
}
return 0;
}
extern int my_strncmp(const char* a, const char* b, size_t len);
// Parse a non-negative integer.
// result: (output) the resulting non-negative integer
// s: a NUL terminated string
// Return true iff successful.
static inline bool
my_strtoui(int* result, const char* s) {
if (*s == 0)
return false;
int r = 0;
for (;; s++) {
if (*s == 0)
break;
const int old_r = r;
r *= 10;
if (*s < '0' || *s > '9')
return false;
r += *s - '0';
if (r < old_r)
return false;
}
extern bool my_strtoui(int* result, const char* s);
*result = r;
return true;
}
// Return the length of the given unsigned integer when expressed in base 10.
extern unsigned my_uint_len(uintmax_t i);
// Return the length of the given, non-negative integer when expressed in base
// 10.
static inline unsigned
my_int_len(intmax_t i) {
if (!i)
return 1;
int len = 0;
while (i) {
len++;
i /= 10;
}
return len;
}
// Convert a non-negative integer to a string
// Convert an unsigned integer to a string
// output: (output) the resulting string is written here. This buffer must be
// large enough to hold the resulting string. Call |my_int_len| to get the
// large enough to hold the resulting string. Call |my_uint_len| to get the
// required length.
// i: the non-negative integer to serialise.
// i_len: the length of the integer in base 10 (see |my_int_len|).
static inline void
my_itos(char* output, intmax_t i, unsigned i_len) {
for (unsigned index = i_len; index; --index, i /= 10)
output[index - 1] = '0' + (i % 10);
}
// i: the unsigned integer to serialise.
// i_len: the length of the integer in base 10 (see |my_uint_len|).
extern void my_uitos(char* output, uintmax_t i, unsigned i_len);
static inline const char*
my_strchr(const char* haystack, char needle) {
while (*haystack && *haystack != needle)
haystack++;
if (*haystack == needle)
return haystack;
return (const char*) 0;
}
extern const char* my_strchr(const char* haystack, char needle);
extern const char* my_strrchr(const char* haystack, char needle);
// Read a hex value
// result: (output) the resulting value
// s: a string
// Returns a pointer to the first invalid charactor.
static inline const char*
my_read_hex_ptr(uintptr_t* result, const char* s) {
uintptr_t r = 0;
extern const char* my_read_hex_ptr(uintptr_t* result, const char* s);
for (;; ++s) {
if (*s >= '0' && *s <= '9') {
r <<= 4;
r += *s - '0';
} else if (*s >= 'a' && *s <= 'f') {
r <<= 4;
r += (*s - 'a') + 10;
} else if (*s >= 'A' && *s <= 'F') {
r <<= 4;
r += (*s - 'A') + 10;
} else {
break;
}
}
extern const char* my_read_decimal_ptr(uintptr_t* result, const char* s);
*result = r;
return s;
}
extern void my_memset(void* ip, char c, size_t len);
static inline void
my_memset(void* ip, char c, size_t len) {
char* p = (char *) ip;
while (len--)
*p++ = c;
}
extern void* my_memchr(const void* src, int c, size_t len);
// The following are considered safe to use in a compromised environment.
// Besides, this gives the compiler an opportunity to optimize their calls.
#define my_memcpy memcpy
#define my_memmove memmove
#define my_memcmp memcmp
extern size_t my_strlcpy(char* s1, const char* s2, size_t len);
extern size_t my_strlcat(char* s1, const char* s2, size_t len);
extern int my_isspace(int ch);
} // extern "C"

View File

@@ -27,8 +27,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "breakpad_googletest_includes.h"
#include "common/linux/linux_libc_support.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
typedef testing::Test LinuxLibcSupportTest;
@@ -89,35 +89,41 @@ TEST(LinuxLibcSupportTest, strtoui) {
ASSERT_EQ(result, 123);
}
TEST(LinuxLibcSupportTest, int_len) {
ASSERT_EQ(my_int_len(0), 1);
ASSERT_EQ(my_int_len(2), 1);
ASSERT_EQ(my_int_len(5), 1);
ASSERT_EQ(my_int_len(9), 1);
ASSERT_EQ(my_int_len(10), 2);
ASSERT_EQ(my_int_len(99), 2);
ASSERT_EQ(my_int_len(100), 3);
ASSERT_EQ(my_int_len(101), 3);
ASSERT_EQ(my_int_len(1000), 4);
TEST(LinuxLibcSupportTest, uint_len) {
ASSERT_EQ(my_uint_len(0), 1U);
ASSERT_EQ(my_uint_len(2), 1U);
ASSERT_EQ(my_uint_len(5), 1U);
ASSERT_EQ(my_uint_len(9), 1U);
ASSERT_EQ(my_uint_len(10), 2U);
ASSERT_EQ(my_uint_len(99), 2U);
ASSERT_EQ(my_uint_len(100), 3U);
ASSERT_EQ(my_uint_len(101), 3U);
ASSERT_EQ(my_uint_len(1000), 4U);
// 0xFFFFFFFFFFFFFFFF
ASSERT_EQ(my_uint_len(18446744073709551615LLU), 20U);
}
TEST(LinuxLibcSupportTest, itos) {
char buf[10];
TEST(LinuxLibcSupportTest, uitos) {
char buf[32];
my_itos(buf, 0, 1);
my_uitos(buf, 0, 1);
ASSERT_EQ(0, memcmp(buf, "0", 1));
my_itos(buf, 1, 1);
my_uitos(buf, 1, 1);
ASSERT_EQ(0, memcmp(buf, "1", 1));
my_itos(buf, 10, 2);
my_uitos(buf, 10, 2);
ASSERT_EQ(0, memcmp(buf, "10", 2));
my_itos(buf, 63, 2);
my_uitos(buf, 63, 2);
ASSERT_EQ(0, memcmp(buf, "63", 2));
my_itos(buf, 101, 3);
my_uitos(buf, 101, 3);
ASSERT_EQ(0, memcmp(buf, "101", 2));
// 0xFFFFFFFFFFFFFFFF
my_uitos(buf, 18446744073709551615LLU, 20);
ASSERT_EQ(0, memcmp(buf, "18446744073709551615", 20));
}
TEST(LinuxLibcSupportTest, strchr) {
@@ -129,6 +135,35 @@ TEST(LinuxLibcSupportTest, strchr) {
ASSERT_TRUE(my_strchr("abc", 'a'));
ASSERT_TRUE(my_strchr("bcda", 'a'));
ASSERT_TRUE(my_strchr("sdfasdf", 'a'));
static const char abc3[] = "abcabcabc";
ASSERT_EQ(abc3, my_strchr(abc3, 'a'));
}
TEST(LinuxLibcSupportTest, strrchr) {
ASSERT_EQ(NULL, my_strrchr("abc", 'd'));
ASSERT_EQ(NULL, my_strrchr("", 'd'));
ASSERT_EQ(NULL, my_strrchr("efghi", 'd'));
ASSERT_TRUE(my_strrchr("a", 'a'));
ASSERT_TRUE(my_strrchr("abc", 'a'));
ASSERT_TRUE(my_strrchr("bcda", 'a'));
ASSERT_TRUE(my_strrchr("sdfasdf", 'a'));
static const char abc3[] = "abcabcabc";
ASSERT_EQ(abc3 + 6, my_strrchr(abc3, 'a'));
}
TEST(LinuxLibcSupportTest, memchr) {
ASSERT_EQ(NULL, my_memchr("abc", 'd', 3));
ASSERT_EQ(NULL, my_memchr("abcd", 'd', 3));
ASSERT_EQ(NULL, my_memchr("a", 'a', 0));
static const char abc3[] = "abcabcabc";
ASSERT_EQ(abc3, my_memchr(abc3, 'a', 3));
ASSERT_EQ(abc3, my_memchr(abc3, 'a', 9));
ASSERT_EQ(abc3+1, my_memchr(abc3, 'b', 9));
ASSERT_EQ(abc3+2, my_memchr(abc3, 'c', 9));
}
TEST(LinuxLibcSupportTest, read_hex_ptr) {
@@ -136,22 +171,43 @@ TEST(LinuxLibcSupportTest, read_hex_ptr) {
const char* last;
last = my_read_hex_ptr(&result, "");
ASSERT_EQ(result, 0);
ASSERT_EQ(result, 0U);
ASSERT_EQ(*last, 0);
last = my_read_hex_ptr(&result, "0");
ASSERT_EQ(result, 0);
ASSERT_EQ(result, 0U);
ASSERT_EQ(*last, 0);
last = my_read_hex_ptr(&result, "0123");
ASSERT_EQ(result, 0x123);
ASSERT_EQ(result, 0x123U);
ASSERT_EQ(*last, 0);
last = my_read_hex_ptr(&result, "0123a");
ASSERT_EQ(result, 0x123a);
ASSERT_EQ(result, 0x123aU);
ASSERT_EQ(*last, 0);
last = my_read_hex_ptr(&result, "0123a-");
ASSERT_EQ(result, 0x123a);
ASSERT_EQ(result, 0x123aU);
ASSERT_EQ(*last, '-');
}
TEST(LinuxLibcSupportTest, read_decimal_ptr) {
uintptr_t result;
const char* last;
last = my_read_decimal_ptr(&result, "0");
ASSERT_EQ(result, 0U);
ASSERT_EQ(*last, 0);
last = my_read_decimal_ptr(&result, "0123");
ASSERT_EQ(result, 123U);
ASSERT_EQ(*last, 0);
last = my_read_decimal_ptr(&result, "1234");
ASSERT_EQ(result, 1234U);
ASSERT_EQ(*last, 0);
last = my_read_decimal_ptr(&result, "01234-");
ASSERT_EQ(result, 1234U);
ASSERT_EQ(*last, '-');
}

View File

@@ -62,10 +62,7 @@ bool MemoryMappedFile::Map(const char* path) {
return false;
}
#if defined(__ANDROID__)
struct stat st;
if (fstat(fd, &st) != 0) {
#elif defined(__x86_64__)
#if defined(__x86_64__)
struct kernel_stat st;
if (sys_fstat(fd, &st) == -1 || st.st_size < 0) {
#else
@@ -100,7 +97,7 @@ bool MemoryMappedFile::Map(const char* path) {
void MemoryMappedFile::Unmap() {
if (content_.data()) {
sys_munmap(const_cast<u_int8_t*>(content_.data()), content_.length());
sys_munmap(const_cast<uint8_t*>(content_.data()), content_.length());
content_.Set(NULL, 0);
}
}

View File

@@ -37,15 +37,14 @@
#include <string>
#include "breakpad_googletest_includes.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/memory_mapped_file.h"
#include "common/tests/auto_tempdir.h"
#include "common/tests/file_utils.h"
#include "common/using_std_string.h"
using google_breakpad::AutoTempDir;
using google_breakpad::MemoryMappedFile;
using google_breakpad::WriteFile;
using std::string;
namespace {
@@ -54,7 +53,7 @@ class MemoryMappedFileTest : public testing::Test {
void ExpectNoMappedData(const MemoryMappedFile& mapped_file) {
EXPECT_TRUE(mapped_file.content().IsEmpty());
EXPECT_TRUE(mapped_file.data() == NULL);
EXPECT_EQ(0, mapped_file.size());
EXPECT_EQ(0U, mapped_file.size());
}
};

View File

@@ -48,7 +48,7 @@ TEST(SafeReadLinkTest, BoundaryBufferSize) {
char buffer[PATH_MAX];
EXPECT_TRUE(SafeReadLink("/proc/self/exe", buffer, sizeof(buffer)));
size_t path_length = strlen(buffer);
EXPECT_LT(0, path_length);
EXPECT_LT(0U, path_length);
EXPECT_GT(sizeof(buffer), path_length);
// Buffer size equals to the expected path length plus 1 for the NULL byte.

View File

@@ -5,19 +5,19 @@
#include <stdio.h>
#include <string.h>
#include "common/linux/elf_gnu_compat.h"
#include "common/using_std_string.h"
namespace google_breakpad {
namespace synth_elf {
#ifndef NT_GNU_BUILD_ID
#define NT_GNU_BUILD_ID 3
#endif
ELF::ELF(uint16_t machine,
uint8_t file_class,
Endianness endianness)
: Section(endianness),
addr_size_(file_class == ELFCLASS64 ? 8 : 4),
program_count_(0),
program_header_table_(endianness),
section_count_(0),
section_header_table_(endianness),
section_header_strings_(endianness) {
@@ -113,18 +113,75 @@ int ELF::AddSection(const string& name, const Section& section,
// sh_entsize
.Append(endianness(), addr_size_, entsize);
sections_.push_back(ElfSection(section, type, addr, offset, offset_label,
size));
return index;
}
void ELF::AppendSection(ElfSection &section) {
// NULL and NOBITS sections have no content, so they
// don't need to be written to the file.
if (type == SHT_NULL) {
offset_label = 0;
} else if (type == SHT_NOBITS) {
offset_label = offset;
if (section.type_ == SHT_NULL) {
section.offset_label_ = 0;
} else if (section.type_ == SHT_NOBITS) {
section.offset_label_ = section.offset_;
} else {
Mark(&offset_label);
Mark(&section.offset_label_);
Append(section);
Align(4);
}
return index;
}
void ELF::AddSegment(int start, int end, uint32_t type, uint32_t flags) {
assert(start > 0);
assert(size_t(start) < sections_.size());
assert(end > 0);
assert(size_t(end) < sections_.size());
++program_count_;
// p_type
program_header_table_.D32(type);
if (addr_size_ == 8) {
// p_flags
program_header_table_.D32(flags);
}
size_t filesz = 0;
size_t memsz = 0;
bool prev_was_nobits = false;
for (int i = start; i <= end; ++i) {
size_t size = sections_[i].size_;
if (sections_[i].type_ != SHT_NOBITS) {
assert(!prev_was_nobits);
// non SHT_NOBITS sections are 4-byte aligned (see AddSection)
size = (size + 3) & ~3;
filesz += size;
} else {
prev_was_nobits = true;
}
memsz += size;
}
program_header_table_
// p_offset
.Append(endianness(), addr_size_, sections_[start].offset_label_)
// p_vaddr
.Append(endianness(), addr_size_, sections_[start].addr_)
// p_paddr
.Append(endianness(), addr_size_, sections_[start].addr_)
// p_filesz
.Append(endianness(), addr_size_, filesz)
// p_memsz
.Append(endianness(), addr_size_, memsz);
if (addr_size_ == 4) {
// p_flags
program_header_table_.D32(flags);
}
// p_align
program_header_table_.Append(endianness(), addr_size_, 0);
}
void ELF::Finish() {
@@ -134,10 +191,19 @@ void ELF::Finish() {
AddSection(".shstrtab", section_header_strings_, SHT_STRTAB);
//printf("section_count_: %ld, sections_.size(): %ld\n",
// section_count_, sections_.size());
if (program_count_) {
Mark(&program_header_label_);
Append(program_header_table_);
} else {
program_header_label_ = 0;
}
for (vector<ElfSection>::iterator it = sections_.begin();
it < sections_.end(); ++it) {
AppendSection(*it);
}
section_count_label_ = section_count_;
program_count_label_ = program_count_;
// TODO: allow adding entries to program header table
program_header_label_ = 0;
// Section header table starts here.
Mark(&section_header_label_);
@@ -174,30 +240,21 @@ void SymbolTable::AddSymbol(const string& name, uint64_t value,
D64(size);
}
BuildIDNote::BuildIDNote(const uint8_t* id_bytes,
size_t id_size,
Endianness endianness) : Section(endianness) {
const char kNoteName[] = "GNU";
void Notes::AddNote(int type, const string &name, const uint8_t* desc_bytes,
size_t desc_size) {
// Elf32_Nhdr and Elf64_Nhdr are exactly the same.
Elf32_Nhdr note_header;
memset(&note_header, 0, sizeof(note_header));
note_header.n_namesz = sizeof(kNoteName);
note_header.n_descsz = id_size;
note_header.n_type = NT_GNU_BUILD_ID;
note_header.n_namesz = name.length() + 1;
note_header.n_descsz = desc_size;
note_header.n_type = type;
Append(reinterpret_cast<const uint8_t*>(&note_header),
sizeof(note_header));
AppendCString(kNoteName);
Append(id_bytes, id_size);
}
// static
void BuildIDNote::AppendSection(ELF& elf,
const uint8_t* id_bytes,
size_t id_size) {
const char kBuildIDSectionName[] = ".note.gnu.build-id";
BuildIDNote note(id_bytes, id_size, elf.endianness());
elf.AddSection(kBuildIDSectionName, note, SHT_NOTE);
AppendCString(name);
Align(4);
Append(desc_bytes, desc_size);
Align(4);
}
} // namespace synth_elf

View File

@@ -39,17 +39,20 @@
#include "common/test_assembler.h"
#include <list>
#include <vector>
#include <map>
#include <string>
#include <utility>
#include "common/using_std_string.h"
namespace google_breakpad {
namespace synth_elf {
using std::list;
using std::vector;
using std::map;
using std::pair;
using std::string;
using test_assembler::Endianness;
using test_assembler::kLittleEndian;
using test_assembler::kUnsetEndian;
@@ -101,6 +104,10 @@ class ELF : public Section {
uint32_t type, uint32_t flags = 0, uint64_t addr = 0,
uint32_t link = 0, uint64_t entsize = 0, uint64_t offset = 0);
// Add a segment containing from section index start to section index end.
// The indexes must have been gotten from AddSection.
void AddSegment(int start, int end, uint32_t type, uint32_t flags = 0);
// Write out all data. GetContents may be used after this.
void Finish();
@@ -113,6 +120,8 @@ class ELF : public Section {
// Number of entries in the program header table.
int program_count_;
Label program_count_label_;
// The program header table itself.
Section program_header_table_;
// Offset to the section header table.
Label section_header_label_;
@@ -127,6 +136,25 @@ class ELF : public Section {
Label section_header_string_index_;
// Section containing the names of section header table entries.
StringTable section_header_strings_;
// Record of an added section
struct ElfSection : public Section {
ElfSection(const Section& section, uint32_t type, uint32_t addr,
uint32_t offset, Label offset_label, uint32_t size)
: Section(section), type_(type), addr_(addr), offset_(offset)
, offset_label_(offset_label), size_(size) {
}
uint32_t type_;
uint32_t addr_;
uint32_t offset_;
Label offset_label_;
uint32_t size_;
};
vector<ElfSection> sections_;
void AppendSection(ElfSection &section);
};
// A class to build .symtab or .dynsym sections.
@@ -149,13 +177,16 @@ class SymbolTable : public Section {
StringTable& table_;
};
// A class to build GNU Build ID note sections
class BuildIDNote : public Section {
// A class for note sections
class Notes : public Section {
public:
BuildIDNote(const uint8_t* id_bytes, size_t id_size, Endianness endianness);
Notes(Endianness endianness)
: Section(endianness) {
}
// Append a new Build ID note section to |elf|.
static void AppendSection(ELF& elf, const uint8_t* id_bytes, size_t id_size);
// Add a note.
void AddNote(int type, const string &name, const uint8_t* desc_bytes,
size_t desc_size);
};
} // namespace synth_elf

View File

@@ -35,17 +35,23 @@
#include <elf.h>
#include "breakpad_googletest_includes.h"
#include "common/linux/elfutils.h"
#include "common/linux/synth_elf.h"
#include "common/using_std_string.h"
using google_breakpad::ElfClass32;
using google_breakpad::ElfClass64;
using google_breakpad::synth_elf::ELF;
using google_breakpad::synth_elf::Notes;
using google_breakpad::synth_elf::Section;
using google_breakpad::synth_elf::StringTable;
using google_breakpad::synth_elf::SymbolTable;
using google_breakpad::test_assembler::Endianness;
using google_breakpad::test_assembler::kBigEndian;
using google_breakpad::test_assembler::kLittleEndian;
using google_breakpad::test_assembler::Label;
using std::string;
using ::testing::Test;
using ::testing::Types;
class StringTableTest : public Test {
public:
@@ -55,7 +61,7 @@ public:
};
TEST_F(StringTableTest, Empty) {
EXPECT_EQ(1, table.Size());
EXPECT_EQ(1U, table.Size());
string contents;
ASSERT_TRUE(table.GetContents(&contents));
const char* kExpectedContents = "\0";
@@ -63,7 +69,7 @@ TEST_F(StringTableTest, Empty) {
contents.c_str(),
contents.size()));
ASSERT_TRUE(table.empty_string.IsKnownConstant());
EXPECT_EQ(0, table.empty_string.Value());
EXPECT_EQ(0U, table.empty_string.Value());
}
TEST_F(StringTableTest, Basic) {
@@ -84,7 +90,7 @@ TEST_F(StringTableTest, Basic) {
contents.size()));
// empty_string is at zero, other strings start at 1.
ASSERT_TRUE(l1.IsKnownConstant());
EXPECT_EQ(1, l1.Value());
EXPECT_EQ(1U, l1.Value());
// Each string has an extra byte for a trailing null.
EXPECT_EQ(1 + s1.length() + 1, l2.Value());
EXPECT_EQ(1 + s1.length() + 1 + s2.length() + 1, l3.Value());
@@ -105,7 +111,7 @@ TEST_F(StringTableTest, Duplicates) {
EXPECT_EQ(0, memcmp(kExpectedContents,
contents.c_str(),
contents.size()));
EXPECT_EQ(0, table.empty_string.Value());
EXPECT_EQ(0U, table.empty_string.Value());
EXPECT_EQ(table.empty_string.Value(), l3.Value());
EXPECT_EQ(l2.Value(), l4.Value());
}
@@ -178,88 +184,230 @@ TEST_F(SymbolTableTest, Simple32) {
symbol_contents.size()));
}
template<typename ElfClass>
class BasicElf : public Test {};
// Doesn't seem worthwhile writing the tests to be endian-independent
// when they're unlikely to ever be run on big-endian systems.
#if defined(__i386__) || defined(__x86_64__)
TEST_F(BasicElf, EmptyLE32) {
typedef Types<ElfClass32, ElfClass64> ElfClasses;
TYPED_TEST_CASE(BasicElf, ElfClasses);
TYPED_TEST(BasicElf, EmptyLE) {
typedef typename TypeParam::Ehdr Ehdr;
typedef typename TypeParam::Phdr Phdr;
typedef typename TypeParam::Shdr Shdr;
const size_t kStringTableSize = sizeof("\0.shstrtab");
const size_t kStringTableAlign = 4 - kStringTableSize % 4;
const size_t kExpectedSize = sizeof(Elf32_Ehdr) +
const size_t kExpectedSize = sizeof(Ehdr) +
// Two sections, SHT_NULL + the section header string table.
2 * sizeof(Elf32_Shdr) +
2 * sizeof(Shdr) +
kStringTableSize + kStringTableAlign;
ELF elf(EM_386, ELFCLASS32, kLittleEndian);
// It doesn't really matter that the machine type is right for the class.
ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
elf.Finish();
EXPECT_EQ(kExpectedSize, elf.Size());
string contents;
ASSERT_TRUE(elf.GetContents(&contents));
ASSERT_EQ(kExpectedSize, contents.size());
const Elf32_Ehdr* header =
reinterpret_cast<const Elf32_Ehdr*>(contents.data());
const Ehdr* header =
reinterpret_cast<const Ehdr*>(contents.data());
const uint8_t kIdent[] = {
ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3,
ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_SYSV,
TypeParam::kClass, ELFDATA2LSB, EV_CURRENT, ELFOSABI_SYSV,
0, 0, 0, 0, 0, 0, 0, 0
};
EXPECT_EQ(0, memcmp(kIdent, header->e_ident, sizeof(kIdent)));
EXPECT_EQ(ET_EXEC, header->e_type);
EXPECT_EQ(EM_386, header->e_machine);
EXPECT_EQ(EV_CURRENT, header->e_version);
EXPECT_EQ(0, header->e_entry);
EXPECT_EQ(0, header->e_phoff);
EXPECT_EQ(sizeof(Elf32_Ehdr) + kStringTableSize + kStringTableAlign,
EXPECT_EQ(static_cast<unsigned int>(EV_CURRENT), header->e_version);
EXPECT_EQ(0U, header->e_entry);
EXPECT_EQ(0U, header->e_phoff);
EXPECT_EQ(sizeof(Ehdr) + kStringTableSize + kStringTableAlign,
header->e_shoff);
EXPECT_EQ(0, header->e_flags);
EXPECT_EQ(sizeof(Elf32_Ehdr), header->e_ehsize);
EXPECT_EQ(sizeof(Elf32_Phdr), header->e_phentsize);
EXPECT_EQ(0U, header->e_flags);
EXPECT_EQ(sizeof(Ehdr), header->e_ehsize);
EXPECT_EQ(sizeof(Phdr), header->e_phentsize);
EXPECT_EQ(0, header->e_phnum);
EXPECT_EQ(sizeof(Elf32_Shdr), header->e_shentsize);
EXPECT_EQ(sizeof(Shdr), header->e_shentsize);
EXPECT_EQ(2, header->e_shnum);
EXPECT_EQ(1, header->e_shstrndx);
const Shdr* shdr =
reinterpret_cast<const Shdr*>(contents.data() + header->e_shoff);
EXPECT_EQ(0U, shdr[0].sh_name);
EXPECT_EQ(static_cast<unsigned int>(SHT_NULL), shdr[0].sh_type);
EXPECT_EQ(0U, shdr[0].sh_flags);
EXPECT_EQ(0U, shdr[0].sh_addr);
EXPECT_EQ(0U, shdr[0].sh_offset);
EXPECT_EQ(0U, shdr[0].sh_size);
EXPECT_EQ(0U, shdr[0].sh_link);
EXPECT_EQ(0U, shdr[0].sh_info);
EXPECT_EQ(0U, shdr[0].sh_addralign);
EXPECT_EQ(0U, shdr[0].sh_entsize);
EXPECT_EQ(1U, shdr[1].sh_name);
EXPECT_EQ(static_cast<unsigned int>(SHT_STRTAB), shdr[1].sh_type);
EXPECT_EQ(0U, shdr[1].sh_flags);
EXPECT_EQ(0U, shdr[1].sh_addr);
EXPECT_EQ(sizeof(Ehdr), shdr[1].sh_offset);
EXPECT_EQ(kStringTableSize, shdr[1].sh_size);
EXPECT_EQ(0U, shdr[1].sh_link);
EXPECT_EQ(0U, shdr[1].sh_info);
EXPECT_EQ(0U, shdr[1].sh_addralign);
EXPECT_EQ(0U, shdr[1].sh_entsize);
}
TEST_F(BasicElf, EmptyLE64) {
const size_t kStringTableSize = sizeof("\0.shstrtab");
TYPED_TEST(BasicElf, BasicLE) {
typedef typename TypeParam::Ehdr Ehdr;
typedef typename TypeParam::Phdr Phdr;
typedef typename TypeParam::Shdr Shdr;
const size_t kStringTableSize = sizeof("\0.text\0.bss\0.shstrtab");
const size_t kStringTableAlign = 4 - kStringTableSize % 4;
const size_t kExpectedSize = sizeof(Elf64_Ehdr) +
// Two sections, SHT_NULL + the section header string table.
2 * sizeof(Elf64_Shdr) +
const size_t kExpectedSize = sizeof(Ehdr) +
// Four sections, SHT_NULL + the section header string table +
// 4096 bytes of the size-aligned .text section + one program header.
sizeof(Phdr) + 4 * sizeof(Shdr) + 4096 +
kStringTableSize + kStringTableAlign;
ELF elf(EM_X86_64, ELFCLASS64, kLittleEndian);
// It doesn't really matter that the machine type is right for the class.
ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
Section text(kLittleEndian);
text.Append(4094, 0);
int text_idx = elf.AddSection(".text", text, SHT_PROGBITS);
Section bss(kLittleEndian);
bss.Append(16, 0);
int bss_idx = elf.AddSection(".bss", bss, SHT_NOBITS);
elf.AddSegment(text_idx, bss_idx, PT_LOAD);
elf.Finish();
EXPECT_EQ(kExpectedSize, elf.Size());
string contents;
ASSERT_TRUE(elf.GetContents(&contents));
ASSERT_EQ(kExpectedSize, contents.size());
const Elf64_Ehdr* header =
reinterpret_cast<const Elf64_Ehdr*>(contents.data());
const Ehdr* header =
reinterpret_cast<const Ehdr*>(contents.data());
const uint8_t kIdent[] = {
ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3,
ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_SYSV,
TypeParam::kClass, ELFDATA2LSB, EV_CURRENT, ELFOSABI_SYSV,
0, 0, 0, 0, 0, 0, 0, 0
};
EXPECT_EQ(0, memcmp(kIdent, header->e_ident, sizeof(kIdent)));
EXPECT_EQ(ET_EXEC, header->e_type);
EXPECT_EQ(EM_X86_64, header->e_machine);
EXPECT_EQ(EV_CURRENT, header->e_version);
EXPECT_EQ(0, header->e_entry);
EXPECT_EQ(0, header->e_phoff);
EXPECT_EQ(sizeof(Elf64_Ehdr) + kStringTableSize + kStringTableAlign,
header->e_shoff);
EXPECT_EQ(0, header->e_flags);
EXPECT_EQ(sizeof(Elf64_Ehdr), header->e_ehsize);
EXPECT_EQ(sizeof(Elf64_Phdr), header->e_phentsize);
EXPECT_EQ(0, header->e_phnum);
EXPECT_EQ(sizeof(Elf64_Shdr), header->e_shentsize);
EXPECT_EQ(2, header->e_shnum);
EXPECT_EQ(1, header->e_shstrndx);
EXPECT_EQ(EM_386, header->e_machine);
EXPECT_EQ(static_cast<unsigned int>(EV_CURRENT), header->e_version);
EXPECT_EQ(0U, header->e_entry);
EXPECT_EQ(sizeof(Ehdr), header->e_phoff);
EXPECT_EQ(sizeof(Ehdr) + sizeof(Phdr) + 4096 + kStringTableSize +
kStringTableAlign, header->e_shoff);
EXPECT_EQ(0U, header->e_flags);
EXPECT_EQ(sizeof(Ehdr), header->e_ehsize);
EXPECT_EQ(sizeof(Phdr), header->e_phentsize);
EXPECT_EQ(1, header->e_phnum);
EXPECT_EQ(sizeof(Shdr), header->e_shentsize);
EXPECT_EQ(4, header->e_shnum);
EXPECT_EQ(3, header->e_shstrndx);
const Shdr* shdr =
reinterpret_cast<const Shdr*>(contents.data() + header->e_shoff);
EXPECT_EQ(0U, shdr[0].sh_name);
EXPECT_EQ(static_cast<unsigned int>(SHT_NULL), shdr[0].sh_type);
EXPECT_EQ(0U, shdr[0].sh_flags);
EXPECT_EQ(0U, shdr[0].sh_addr);
EXPECT_EQ(0U, shdr[0].sh_offset);
EXPECT_EQ(0U, shdr[0].sh_size);
EXPECT_EQ(0U, shdr[0].sh_link);
EXPECT_EQ(0U, shdr[0].sh_info);
EXPECT_EQ(0U, shdr[0].sh_addralign);
EXPECT_EQ(0U, shdr[0].sh_entsize);
EXPECT_EQ(1U, shdr[1].sh_name);
EXPECT_EQ(static_cast<unsigned int>(SHT_PROGBITS), shdr[1].sh_type);
EXPECT_EQ(0U, shdr[1].sh_flags);
EXPECT_EQ(0U, shdr[1].sh_addr);
EXPECT_EQ(sizeof(Ehdr) + sizeof(Phdr), shdr[1].sh_offset);
EXPECT_EQ(4094U, shdr[1].sh_size);
EXPECT_EQ(0U, shdr[1].sh_link);
EXPECT_EQ(0U, shdr[1].sh_info);
EXPECT_EQ(0U, shdr[1].sh_addralign);
EXPECT_EQ(0U, shdr[1].sh_entsize);
EXPECT_EQ(sizeof("\0.text"), shdr[2].sh_name);
EXPECT_EQ(static_cast<unsigned int>(SHT_NOBITS), shdr[2].sh_type);
EXPECT_EQ(0U, shdr[2].sh_flags);
EXPECT_EQ(0U, shdr[2].sh_addr);
EXPECT_EQ(0U, shdr[2].sh_offset);
EXPECT_EQ(16U, shdr[2].sh_size);
EXPECT_EQ(0U, shdr[2].sh_link);
EXPECT_EQ(0U, shdr[2].sh_info);
EXPECT_EQ(0U, shdr[2].sh_addralign);
EXPECT_EQ(0U, shdr[2].sh_entsize);
EXPECT_EQ(sizeof("\0.text\0.bss"), shdr[3].sh_name);
EXPECT_EQ(static_cast<unsigned int>(SHT_STRTAB), shdr[3].sh_type);
EXPECT_EQ(0U, shdr[3].sh_flags);
EXPECT_EQ(0U, shdr[3].sh_addr);
EXPECT_EQ(sizeof(Ehdr) + sizeof(Phdr) + 4096, shdr[3].sh_offset);
EXPECT_EQ(kStringTableSize, shdr[3].sh_size);
EXPECT_EQ(0U, shdr[3].sh_link);
EXPECT_EQ(0U, shdr[3].sh_info);
EXPECT_EQ(0U, shdr[3].sh_addralign);
EXPECT_EQ(0U, shdr[3].sh_entsize);
const Phdr* phdr =
reinterpret_cast<const Phdr*>(contents.data() + header->e_phoff);
EXPECT_EQ(static_cast<unsigned int>(PT_LOAD), phdr->p_type);
EXPECT_EQ(sizeof(Ehdr) + sizeof(Phdr), phdr->p_offset);
EXPECT_EQ(0U, phdr->p_vaddr);
EXPECT_EQ(0U, phdr->p_paddr);
EXPECT_EQ(4096U, phdr->p_filesz);
EXPECT_EQ(4096U + 16U, phdr->p_memsz);
EXPECT_EQ(0U, phdr->p_flags);
EXPECT_EQ(0U, phdr->p_align);
}
class ElfNotesTest : public Test {};
TEST_F(ElfNotesTest, Empty) {
Notes notes(kLittleEndian);
string contents;
ASSERT_TRUE(notes.GetContents(&contents));
EXPECT_EQ(0U, contents.size());
}
TEST_F(ElfNotesTest, Notes) {
Notes notes(kLittleEndian);
notes.AddNote(1, "Linux", reinterpret_cast<const uint8_t *>("\x42\x02\0\0"),
4);
notes.AddNote(2, "a", reinterpret_cast<const uint8_t *>("foobar"),
sizeof("foobar") - 1);
const uint8_t kExpectedNotesContents[] = {
// Note 1
0x06, 0x00, 0x00, 0x00, // name size, including terminating zero
0x04, 0x00, 0x00, 0x00, // desc size
0x01, 0x00, 0x00, 0x00, // type
'L', 'i', 'n', 'u', 'x', 0x00, 0x00, 0x00, // padded "Linux"
0x42, 0x02, 0x00, 0x00, // desc
// Note 2
0x02, 0x00, 0x00, 0x00, // name size
0x06, 0x00, 0x00, 0x00, // desc size
0x02, 0x00, 0x00, 0x00, // type
'a', 0x00, 0x00, 0x00, // padded "a"
'f', 'o', 'o', 'b', 'a', 'r', 0x00, 0x00, // padded "foobar"
};
const size_t kExpectedNotesSize = sizeof(kExpectedNotesContents);
EXPECT_EQ(kExpectedNotesSize, notes.Size());
string notes_contents;
ASSERT_TRUE(notes.GetContents(&notes_contents));
EXPECT_EQ(0, memcmp(kExpectedNotesContents,
notes_contents.data(),
notes_contents.size()));
}
#endif // defined(__i386__) || defined(__x86_64__)

View File

@@ -0,0 +1,124 @@
// Copyright (c) 2013, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Utility class for creating a temporary file for unit tests
// that is deleted in the destructor.
#ifndef GOOGLE_BREAKPAD_COMMON_LINUX_TESTS_AUTO_TESTFILE
#define GOOGLE_BREAKPAD_COMMON_LINUX_TESTS_AUTO_TESTFILE
#include <unistd.h>
#include <sys/types.h>
#include <string>
#include "breakpad_googletest_includes.h"
#include "common/linux/eintr_wrapper.h"
#include "common/tests/auto_tempdir.h"
namespace google_breakpad {
class AutoTestFile {
public:
// Create a new empty test file.
// test_prefix: (input) test-specific prefix, can't be NULL.
explicit AutoTestFile(const char* test_prefix) {
Init(test_prefix);
}
// Create a new test file, and fill it with initial data from a C string.
// The terminating zero is not written.
// test_prefix: (input) test-specific prefix, can't be NULL.
// text: (input) initial content.
AutoTestFile(const char* test_prefix, const char* text) {
Init(test_prefix);
if (fd_ >= 0)
WriteText(text, static_cast<size_t>(strlen(text)));
}
AutoTestFile(const char* test_prefix, const char* text, size_t text_len) {
Init(test_prefix);
if (fd_ >= 0)
WriteText(text, text_len);
}
// Destroy test file on scope exit.
~AutoTestFile() {
if (fd_ >= 0) {
close(fd_);
fd_ = -1;
}
}
// Returns true iff the test file could be created properly.
// Useful in tests inside EXPECT_TRUE(file.IsOk());
bool IsOk() {
return fd_ >= 0;
}
// Returns the Posix file descriptor for the test file, or -1
// If IsOk() returns false. Note: on Windows, this always returns -1.
int GetFd() {
return fd_;
}
private:
void Init(const char* test_prefix) {
fd_ = -1;
char path_templ[PATH_MAX];
int ret = snprintf(path_templ, sizeof(path_templ),
TEMPDIR "/%s-unittest.XXXXXX",
test_prefix);
if (ret >= static_cast<int>(sizeof(path_templ)))
return;
fd_ = mkstemp(path_templ);
if (fd_ < 0)
return;
unlink(path_templ);
}
void WriteText(const char* text, size_t text_len) {
ssize_t r = HANDLE_EINTR(write(fd_, text, text_len));
if (r != static_cast<ssize_t>(text_len)) {
close(fd_);
fd_ = -1;
return;
}
lseek(fd_, 0, SEEK_SET);
}
int fd_;
};
} // namespace google_breakpad
#endif // GOOGLE_BREAKPAD_COMMON_LINUX_TESTS_AUTO_TESTFILE

View File

@@ -43,9 +43,13 @@
#include <string>
#if defined(__ANDROID__)
#include "common/android/testing/pthread_fixes.h"
#endif
#include "common/linux/eintr_wrapper.h"
#include "common/tests/auto_tempdir.h"
#include "common/tests/file_utils.h"
#include "common/using_std_string.h"
namespace {
@@ -97,11 +101,11 @@ bool CrashGenerator::HasDefaultCorePattern() const {
buffer_size == 5 && memcmp(buffer, "core", 4) == 0;
}
std::string CrashGenerator::GetCoreFilePath() const {
string CrashGenerator::GetCoreFilePath() const {
return temp_dir_.path() + "/core";
}
std::string CrashGenerator::GetDirectoryOfProcFilesCopy() const {
string CrashGenerator::GetDirectoryOfProcFilesCopy() const {
return temp_dir_.path() + "/proc";
}
@@ -170,7 +174,7 @@ bool CrashGenerator::CreateChildCrash(
}
if (SetCoreFileSizeLimit(kCoreSizeLimit)) {
CreateThreadsInChildProcess(num_threads);
std::string proc_dir = GetDirectoryOfProcFilesCopy();
string proc_dir = GetDirectoryOfProcFilesCopy();
if (mkdir(proc_dir.c_str(), 0755) == -1) {
perror("CrashGenerator: Failed to create proc directory");
exit(1);

View File

@@ -38,6 +38,7 @@
#include <string>
#include "common/tests/auto_tempdir.h"
#include "common/using_std_string.h"
namespace google_breakpad {
@@ -59,10 +60,10 @@ class CrashGenerator {
bool HasDefaultCorePattern() const;
// Returns the expected path of the core dump file.
std::string GetCoreFilePath() const;
string GetCoreFilePath() const;
// Returns the directory of a copy of proc files of the child process.
std::string GetDirectoryOfProcFilesCopy() const;
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

View File

@@ -31,3 +31,4 @@
GCC_OPTIMIZATION_LEVEL = s
GCC_WARN_UNINITIALIZED_AUTOS = YES
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) NDEBUG

View File

@@ -1,4 +1,4 @@
//
//
// GTMDefines.h
//
// Copyright 2008 Google Inc.
@@ -6,21 +6,29 @@
// 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 <AvailabilityMacros.h>
#include <TargetConditionals.h>
#ifdef __OBJC__
#include <Foundation/NSObjCRuntime.h>
#endif // __OBJC__
#if TARGET_OS_IPHONE
#include <Availability.h>
#endif // TARGET_OS_IPHONE
// Not all MAC_OS_X_VERSION_10_X macros defined in past SDKs
#ifndef MAC_OS_X_VERSION_10_5
#define MAC_OS_X_VERSION_10_5 1050
@@ -28,6 +36,29 @@
#ifndef MAC_OS_X_VERSION_10_6
#define MAC_OS_X_VERSION_10_6 1060
#endif
#ifndef MAC_OS_X_VERSION_10_7
#define MAC_OS_X_VERSION_10_7 1070
#endif
// Not all __IPHONE_X macros defined in past SDKs
#ifndef __IPHONE_3_0
#define __IPHONE_3_0 30000
#endif
#ifndef __IPHONE_3_1
#define __IPHONE_3_1 30100
#endif
#ifndef __IPHONE_3_2
#define __IPHONE_3_2 30200
#endif
#ifndef __IPHONE_4_0
#define __IPHONE_4_0 40000
#endif
#ifndef __IPHONE_4_3
#define __IPHONE_4_3 40300
#endif
#ifndef __IPHONE_5_0
#define __IPHONE_5_0 50000
#endif
// ----------------------------------------------------------------------------
// CPP symbols that can be overridden in a prefix to control how the toolbox
@@ -35,7 +66,7 @@
// ----------------------------------------------------------------------------
// By setting the GTM_CONTAINERS_VALIDATION_FAILED_LOG and
// By setting the GTM_CONTAINERS_VALIDATION_FAILED_LOG and
// GTM_CONTAINERS_VALIDATION_FAILED_ASSERT macros you can control what happens
// when a validation fails. If you implement your own validators, you may want
// to control their internals using the same macros for consistency.
@@ -47,7 +78,7 @@
// a few different actual definitions, so we're based off of the foundation
// one.
#if !defined(GTM_INLINE)
#if defined (__GNUC__) && (__GNUC__ == 4)
#if (defined (__GNUC__) && (__GNUC__ == 4)) || defined (__clang__)
#define GTM_INLINE static __inline__ __attribute__((always_inline))
#else
#define GTM_INLINE static __inline__
@@ -59,8 +90,12 @@
#if !defined (GTM_EXTERN)
#if defined __cplusplus
#define GTM_EXTERN extern "C"
#define GTM_EXTERN_C_BEGIN extern "C" {
#define GTM_EXTERN_C_END }
#else
#define GTM_EXTERN extern
#define GTM_EXTERN_C_BEGIN
#define GTM_EXTERN_C_END
#endif
#endif
@@ -70,6 +105,12 @@
#define GTM_EXPORT __attribute__((visibility("default")))
#endif
// Give ourselves a consistent way of declaring something as unused. This
// doesn't use __unused because that is only supported in gcc 4.2 and greater.
#if !defined (GTM_UNUSED)
#define GTM_UNUSED(x) ((void)(x))
#endif
// _GTMDevLog & _GTMDevAssert
//
// _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for
@@ -82,12 +123,12 @@
// _GTMDevLog log some error/problem in debug builds
// _GTMDevAssert assert if conditon isn't met w/in a method/function
// in all builds.
//
//
// To replace this system, just provide different macro definitions in your
// prefix header. Remember, any implementation you provide *must* be thread
// safe since this could be called by anything in what ever situtation it has
// been placed in.
//
//
// We only define the simple macros if nothing else has defined this.
#ifndef _GTMDevLog
@@ -100,11 +141,6 @@
#endif // _GTMDevLog
// Declared here so that it can easily be used for logging tracking if
// necessary. See GTMUnitTestDevLog.h for details.
@class NSString;
GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
#ifndef _GTMDevAssert
// we directly invoke the NSAssert handler so we can pass on the varargs
// (NSAssert doesn't have a macro we can use that takes varargs)
@@ -145,28 +181,6 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ]
#endif // _GTMCompileAssert
// Macro to allow fast enumeration when building for 10.5 or later, and
// reliance on NSEnumerator for 10.4. Remember, NSDictionary w/ FastEnumeration
// does keys, so pick the right thing, nothing is done on the FastEnumeration
// side to be sure you're getting what you wanted.
#ifndef GTM_FOREACH_OBJECT
#if TARGET_OS_IPHONE || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
#define GTM_FOREACH_OBJECT(element, collection) \
for (element in collection)
#define GTM_FOREACH_KEY(element, collection) \
for (element in collection)
#else
#define GTM_FOREACH_OBJECT(element, collection) \
for (NSEnumerator * _ ## element ## _enum = [collection objectEnumerator]; \
(element = [_ ## element ## _enum nextObject]) != nil; )
#define GTM_FOREACH_KEY(element, collection) \
for (NSEnumerator * _ ## element ## _enum = [collection keyEnumerator]; \
(element = [_ ## element ## _enum nextObject]) != nil; )
#endif
#endif
// ============================================================================
// ----------------------------------------------------------------------------
// CPP symbols defined based on the project settings so the GTM code has
// simple things to test against w/o scattering the knowledge of project
@@ -183,11 +197,26 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
#else
#define GTM_IPHONE_DEVICE 1
#endif // TARGET_IPHONE_SIMULATOR
// By default, GTM has provided it's own unittesting support, define this
// to use the support provided by Xcode, especially for the Xcode4 support
// for unittesting.
#ifndef GTM_IPHONE_USE_SENTEST
#define GTM_IPHONE_USE_SENTEST 0
#endif
#else
// For MacOS specific stuff
#define GTM_MACOS_SDK 1
#endif
// Some of our own availability macros
#if GTM_MACOS_SDK
#define GTM_AVAILABLE_ONLY_ON_IPHONE UNAVAILABLE_ATTRIBUTE
#define GTM_AVAILABLE_ONLY_ON_MACOS
#else
#define GTM_AVAILABLE_ONLY_ON_IPHONE
#define GTM_AVAILABLE_ONLY_ON_MACOS UNAVAILABLE_ATTRIBUTE
#endif
// Provide a symbol to include/exclude extra code for GC support. (This mainly
// just controls the inclusion of finalize methods).
#ifndef GTM_SUPPORT_GC
@@ -197,7 +226,7 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
#else
// We can't find a symbol to tell if GC is supported/required, so best we
// do on Mac targets is include it if we're on 10.5 or later.
#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
#define GTM_SUPPORT_GC 0
#else
#define GTM_SUPPORT_GC 1
@@ -207,7 +236,7 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
// To simplify support for 64bit (and Leopard in general), we provide the type
// defines for non Leopard SDKs
#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
#if !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
// NSInteger/NSUInteger and Max/Mins
#ifndef NSINTEGER_DEFINED
#if __LP64__ || NS_BUILD_32_LIKE_64
@@ -238,4 +267,178 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
#endif /* !defined(__LP64__) || !__LP64__ */
#define CGFLOAT_DEFINED 1
#endif // CGFLOAT_DEFINED
#endif // MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
#endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
// Some support for advanced clang static analysis functionality
// See http://clang-analyzer.llvm.org/annotations.html
#ifndef __has_feature // Optional.
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif
#ifndef NS_RETURNS_RETAINED
#if __has_feature(attribute_ns_returns_retained)
#define NS_RETURNS_RETAINED __attribute__((ns_returns_retained))
#else
#define NS_RETURNS_RETAINED
#endif
#endif
#ifndef NS_RETURNS_NOT_RETAINED
#if __has_feature(attribute_ns_returns_not_retained)
#define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained))
#else
#define NS_RETURNS_NOT_RETAINED
#endif
#endif
#ifndef CF_RETURNS_RETAINED
#if __has_feature(attribute_cf_returns_retained)
#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
#else
#define CF_RETURNS_RETAINED
#endif
#endif
#ifndef CF_RETURNS_NOT_RETAINED
#if __has_feature(attribute_cf_returns_not_retained)
#define CF_RETURNS_NOT_RETAINED __attribute__((cf_returns_not_retained))
#else
#define CF_RETURNS_NOT_RETAINED
#endif
#endif
#ifndef NS_CONSUMED
#if __has_feature(attribute_ns_consumed)
#define NS_CONSUMED __attribute__((ns_consumed))
#else
#define NS_CONSUMED
#endif
#endif
#ifndef CF_CONSUMED
#if __has_feature(attribute_cf_consumed)
#define CF_CONSUMED __attribute__((cf_consumed))
#else
#define CF_CONSUMED
#endif
#endif
#ifndef NS_CONSUMES_SELF
#if __has_feature(attribute_ns_consumes_self)
#define NS_CONSUMES_SELF __attribute__((ns_consumes_self))
#else
#define NS_CONSUMES_SELF
#endif
#endif
// Defined on 10.6 and above.
#ifndef NS_FORMAT_ARGUMENT
#define NS_FORMAT_ARGUMENT(A)
#endif
// Defined on 10.6 and above.
#ifndef NS_FORMAT_FUNCTION
#define NS_FORMAT_FUNCTION(F,A)
#endif
// Defined on 10.6 and above.
#ifndef CF_FORMAT_ARGUMENT
#define CF_FORMAT_ARGUMENT(A)
#endif
// Defined on 10.6 and above.
#ifndef CF_FORMAT_FUNCTION
#define CF_FORMAT_FUNCTION(F,A)
#endif
#ifndef GTM_NONNULL
#define GTM_NONNULL(x) __attribute__((nonnull(x)))
#endif
// Invalidates the initializer from which it's called.
#ifndef GTMInvalidateInitializer
#if __has_feature(objc_arc)
#define GTMInvalidateInitializer() \
do { \
[self class]; /* Avoid warning of dead store to |self|. */ \
_GTMDevAssert(NO, @"Invalid initializer."); \
return nil; \
} while (0)
#else
#define GTMInvalidateInitializer() \
do { \
[self release]; \
_GTMDevAssert(NO, @"Invalid initializer."); \
return nil; \
} while (0)
#endif
#endif
#ifdef __OBJC__
// Declared here so that it can easily be used for logging tracking if
// necessary. See GTMUnitTestDevLog.h for details.
@class NSString;
GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...) NS_FORMAT_FUNCTION(1, 2);
// Macro to allow you to create NSStrings out of other macros.
// #define FOO foo
// NSString *fooString = GTM_NSSTRINGIFY(FOO);
#if !defined (GTM_NSSTRINGIFY)
#define GTM_NSSTRINGIFY_INNER(x) @#x
#define GTM_NSSTRINGIFY(x) GTM_NSSTRINGIFY_INNER(x)
#endif
// Macro to allow fast enumeration when building for 10.5 or later, and
// reliance on NSEnumerator for 10.4. Remember, NSDictionary w/ FastEnumeration
// does keys, so pick the right thing, nothing is done on the FastEnumeration
// side to be sure you're getting what you wanted.
#ifndef GTM_FOREACH_OBJECT
#if TARGET_OS_IPHONE || !(MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
#define GTM_FOREACH_ENUMEREE(element, enumeration) \
for (element in enumeration)
#define GTM_FOREACH_OBJECT(element, collection) \
for (element in collection)
#define GTM_FOREACH_KEY(element, collection) \
for (element in collection)
#else
#define GTM_FOREACH_ENUMEREE(element, enumeration) \
for (NSEnumerator *_ ## element ## _enum = enumeration; \
(element = [_ ## element ## _enum nextObject]) != nil; )
#define GTM_FOREACH_OBJECT(element, collection) \
GTM_FOREACH_ENUMEREE(element, [collection objectEnumerator])
#define GTM_FOREACH_KEY(element, collection) \
GTM_FOREACH_ENUMEREE(element, [collection keyEnumerator])
#endif
#endif
// ============================================================================
// To simplify support for both Leopard and Snow Leopard we declare
// the Snow Leopard protocols that we need here.
#if !defined(GTM_10_6_PROTOCOLS_DEFINED) && !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
#define GTM_10_6_PROTOCOLS_DEFINED 1
@protocol NSConnectionDelegate
@end
@protocol NSAnimationDelegate
@end
@protocol NSImageDelegate
@end
@protocol NSTabViewDelegate
@end
#endif // !defined(GTM_10_6_PROTOCOLS_DEFINED) && !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
// GTM_SEL_STRING is for specifying selector (usually property) names to KVC
// or KVO methods.
// In debug it will generate warnings for undeclared selectors if
// -Wunknown-selector is turned on.
// In release it will have no runtime overhead.
#ifndef GTM_SEL_STRING
#ifdef DEBUG
#define GTM_SEL_STRING(selName) NSStringFromSelector(@selector(selName))
#else
#define GTM_SEL_STRING(selName) @#selName
#endif // DEBUG
#endif // GTM_SEL_STRING
#endif // __OBJC__

View File

@@ -6,9 +6,9 @@
// 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
@@ -18,15 +18,15 @@
// Key Abstractions
// ----------------
//
// This file declares multiple classes and protocols that are used by the
//
// This file declares multiple classes and protocols that are used by the
// GTMLogger logging system. The 4 main abstractions used in this file are the
// following:
//
// * logger (GTMLogger) - The main logging class that users interact with. It
// has methods for logging at different levels and uses a log writer, a log
// formatter, and a log filter to get the job done.
//
//
// * log writer (GTMLogWriter) - Writes a given string to some log file, where
// a "log file" can be a physical file on disk, a POST over HTTP to some URL,
// or even some in-memory structure (e.g., a ring buffer).
@@ -44,7 +44,7 @@
// flexibility to dynamically enable debug logging in Release builds.
//
// This file also declares some classes to handle the common log writer, log
// formatter, and log filter cases. Callers can also create their own writers,
// formatter, and log filter cases. Callers can also create their own writers,
// formatters, and filters and they can even build them on top of the ones
// declared here. Keep in mind that your custom writer/formatter/filter may be
// called from multiple threads, so it must be thread-safe.
@@ -55,12 +55,6 @@
// Predeclaration of used protocols that are declared later in this file.
@protocol GTMLogWriter, GTMLogFormatter, GTMLogFilter;
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
#define CHECK_FORMAT_NSSTRING(a, b) __attribute__((format(__NSString__, a, b)))
#else
#define CHECK_FORMAT_NSSTRING(a, b)
#endif
// GTMLogger
//
// GTMLogger is the primary user-facing class for an object-oriented logging
@@ -69,7 +63,7 @@
// sent to a GTMLogger to log a message, the message is formatted using the log
// formatter, then the log filter is consulted to see if the message should be
// logged, and if so, the message is sent to the log writer to be written out.
//
//
// GTMLogger is intended to be a flexible and thread-safe logging solution. Its
// flexibility comes from the fact that GTMLogger instances can be customized
// with user defined formatters, filters, and writers. And these writers,
@@ -77,7 +71,7 @@
// ways to suit the needs at hand. For example, multiple writers can be used at
// the same time, and a GTMLogger instance can even be used as another
// GTMLogger's writer. This allows for arbitrarily deep logging trees.
//
//
// A standard GTMLogger uses a writer that sends messages to standard out, a
// formatter that smacks a timestamp and a few other bits of interesting
// information on the message, and a filter that filters out debug messages from
@@ -85,13 +79,13 @@
// the following:
//
// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] foo=<Foo: 0x123>
//
//
// The output contains the date and time of the log message, the name of the
// process followed by its process ID/thread ID, the log level at which the
// message was logged (in the previous example the level was 1:
// kGTMLoggerLevelDebug), and finally, the user-specified log message itself (in
// this case, the log message was @"foo=%@", foo).
//
//
// Multiple instances of GTMLogger can be created, each configured their own
// way. Though GTMLogger is not a singleton (in the GoF sense), it does provide
// access to a shared (i.e., globally accessible) GTMLogger instance. This makes
@@ -113,10 +107,10 @@
// with behavior that many developers are currently used to. Note that this
// means that GTMLoggerDebug(@"hi") will be compiled out of Release builds, but
// [[GTMLogger sharedLogger] logDebug:@"hi"] will NOT be compiled out.
//
//
// Standard loggers are created with the GTMLogLevelFilter log filter, which
// filters out certain log messages based on log level, and some other settings.
//
//
// In addition to the -logDebug:, -logInfo:, and -logError: methods defined on
// GTMLogger itself, there are also C macros that make usage of the shared
// GTMLogger instance very convenient. These macros are:
@@ -146,7 +140,7 @@
// GTMLogger class directly in order to configure the shared logger, which all
// of the code using the macros will be using. Again, this is just the typical
// situation.
//
//
// To be complete, there are cases where you may want to use GTMLogger directly,
// or even create separate GTMLogger instances for some reason. That's fine,
// too.
@@ -160,14 +154,14 @@
//
// GTMLoggerDebug(@"foo = %@", foo);
//
// 2. The previous example is similar to the following. The major difference is
// 2. The previous example is similar to the following. The major difference is
// that the previous call (example 1) will be compiled out of Release builds
// but this statement will not be compiled out.
//
// [[GTMLogger sharedLogger] logDebug:@"foo = %@", foo];
//
// 3. Send all logging output from the shared logger to a file. We do this by
// creating an NSFileHandle for writing associated with a file, and setting
// creating an NSFileHandle for writing associated with a file, and setting
// that file handle as the logger's writer.
//
// NSFileHandle *f = [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log"
@@ -185,12 +179,12 @@
// 5. Create a logger that writes to stdout and does NOT do any formatting to
// the log message. This might be useful, for example, when writing a help
// screen for a command-line tool to standard output.
//
//
// GTMLogger *logger = [GTMLogger logger];
// [logger logInfo:@"%@ version 0.1 usage", progName];
//
// 6. Send log output to stdout AND to a log file. The trick here is that
// NSArrays function as composite log writers, which means when an array is
// 6. Send log output to stdout AND to a log file. The trick here is that
// NSArrays function as composite log writers, which means when an array is
// set as the log writer, it forwards all logging messages to all of its
// contained GTMLogWriters.
//
@@ -198,7 +192,7 @@
// NSArray *writers = [NSArray arrayWithObjects:
// [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log" create:YES],
// [NSFileHandle fileHandleWithStandardOutput], nil];
//
//
// GTMLogger *logger = [GTMLogger standardLogger];
// [logger setWriter:writers];
// [logger logInfo:@"hi"]; // Output goes to stdout and /tmp/f.log
@@ -244,6 +238,10 @@
// Same as +standardLogger, but logs to stderr.
+ (id)standardLoggerWithStderr;
// Same as +standardLogger but levels >= kGTMLoggerLevelError are routed to
// stderr, everything else goes to stdout.
+ (id)standardLoggerWithStdoutAndStderr;
// Returns a new standard GTMLogger instance with a log writer that will
// write to the file at |path|, and will use the GTMLogStandardFormatter and
// GTMLogLevelFilter classes. If |path| does not exist, it will be created.
@@ -274,20 +272,20 @@
//
// Logs a message at the debug level (kGTMLoggerLevelDebug).
- (void)logDebug:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2);
- (void)logDebug:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
// Logs a message at the info level (kGTMLoggerLevelInfo).
- (void)logInfo:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2);
- (void)logInfo:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
// Logs a message at the error level (kGTMLoggerLevelError).
- (void)logError:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2);
- (void)logError:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
// Logs a message at the assert level (kGTMLoggerLevelAssert).
- (void)logAssert:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2);
- (void)logAssert:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
//
// Accessors
//
// Accessor methods for the log writer. If the log writer is set to nil,
// Accessor methods for the log writer. If the log writer is set to nil,
// [NSFileHandle fileHandleWithStandardOutput] is used.
- (id<GTMLogWriter>)writer;
- (void)setWriter:(id<GTMLogWriter>)writer;
@@ -306,20 +304,23 @@
@end // GTMLogger
// Helper functions that are used by the convenience GTMLogger*() macros that
// Helper functions that are used by the convenience GTMLogger*() macros that
// enable the logging of function names.
@interface GTMLogger (GTMLoggerMacroHelpers)
- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ...
CHECK_FORMAT_NSSTRING(2, 3);
NS_FORMAT_FUNCTION(2, 3);
- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ...
CHECK_FORMAT_NSSTRING(2, 3);
NS_FORMAT_FUNCTION(2, 3);
- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ...
CHECK_FORMAT_NSSTRING(2, 3);
NS_FORMAT_FUNCTION(2, 3);
- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ...
CHECK_FORMAT_NSSTRING(2, 3);
NS_FORMAT_FUNCTION(2, 3);
@end // GTMLoggerMacroHelpers
// The convenience macros are only defined if they haven't already been defined.
#ifndef GTMLoggerInfo
// Convenience macros that log to the shared GTMLogger instance. These macros
// are how users should typically log to GTMLogger. Notice that GTMLoggerDebug()
// calls will be compiled out of non-Debug builds.
@@ -339,6 +340,8 @@
#define GTMLoggerDebug(...) do {} while(0)
#endif
#endif // !defined(GTMLoggerInfo)
// Log levels.
typedef enum {
kGTMLoggerLevelUnknown,
@@ -365,7 +368,7 @@ typedef enum {
// now becomes a valid log writer. Log messages are written to the file handle
// with a newline appended.
@interface NSFileHandle (GTMFileHandleLogWriter) <GTMLogWriter>
// Opens the file at |path| in append mode, and creates the file with |mode|
// Opens the file at |path| in append mode, and creates the file with |mode|
// if it didn't previously exist.
+ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode;
@end // NSFileHandle
@@ -379,7 +382,7 @@ typedef enum {
//
// This is useful in situations where you would like to send log output to
// multiple log writers at the same time. Simply create an NSArray of the log
// writers you wish to use, then set the array as the "writer" for your
// writers you wish to use, then set the array as the "writer" for your
// GTMLogger instance.
@interface NSArray (GTMArrayCompositeLogWriter) <GTMLogWriter>
@end // GTMArrayCompositeLogWriter
@@ -390,7 +393,7 @@ typedef enum {
//
// This is useful when you want to configure a logger to log to a specific
// writer with a specific formatter and/or filter. But you want to also compose
// that with a different log writer that may have its own formatter and/or
// that with a different log writer that may have its own formatter and/or
// filter.
@interface GTMLogger (GTMLoggerLogWriter) <GTMLogWriter>
@end // GTMLoggerLogWriter
@@ -407,14 +410,18 @@ typedef enum {
- (NSString *)stringForFunc:(NSString *)func
withFormat:(NSString *)fmt
valist:(va_list)args
level:(GTMLoggerLevel)level;
level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0);
@end // GTMLogFormatter
// A basic log formatter that formats a string the same way that NSLog (or
// A basic log formatter that formats a string the same way that NSLog (or
// printf) would. It does not do anything fancy, nor does it add any data of its
// own.
@interface GTMLogBasicFormatter : NSObject <GTMLogFormatter>
// Helper method for prettying C99 __func__ and GCC __PRETTY_FUNCTION__
- (NSString *)prettyNameForFunc:(NSString *)func;
@end // GTMLogBasicFormatter
@@ -450,9 +457,48 @@ typedef enum {
@interface GTMLogLevelFilter : NSObject <GTMLogFilter>
@end // GTMLogLevelFilter
// A simple log filter that does NOT filter anything out;
// -filterAllowsMessage:level will always return YES. This can be a convenient
// way to enable debug-level logging in release builds (if you so desire).
@interface GTMLogNoFilter : NSObject <GTMLogFilter>
@end // GTMLogNoFilter
// Base class for custom level filters. Not for direct use, use the minimum
// or maximum level subclasses below.
@interface GTMLogAllowedLevelFilter : NSObject <GTMLogFilter> {
@private
NSIndexSet *allowedLevels_;
}
@end
// A log filter that allows you to set a minimum log level. Messages below this
// level will be filtered.
@interface GTMLogMininumLevelFilter : GTMLogAllowedLevelFilter
// Designated initializer, logs at levels < |level| will be filtered.
- (id)initWithMinimumLevel:(GTMLoggerLevel)level;
@end
// A log filter that allows you to set a maximum log level. Messages whose level
// exceeds this level will be filtered. This is really only useful if you have
// a composite GTMLogger that is sending the other messages elsewhere.
@interface GTMLogMaximumLevelFilter : GTMLogAllowedLevelFilter
// Designated initializer, logs at levels > |level| will be filtered.
- (id)initWithMaximumLevel:(GTMLoggerLevel)level;
@end
// For subclasses only
@interface GTMLogger (PrivateMethods)
- (void)logInternalFunc:(const char *)func
format:(NSString *)fmt
valist:(va_list)args
level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0);
@end

View File

@@ -6,9 +6,9 @@
// 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
@@ -24,39 +24,30 @@
#import <pthread.h>
// Define a trivial assertion macro to avoid dependencies
#ifdef DEBUG
#define GTMLOGGER_ASSERT(expr) assert(expr)
#else
#define GTMLOGGER_ASSERT(expr)
#endif
#if !defined(__clang__) && (__GNUC__*10+__GNUC_MINOR__ >= 42)
// Some versions of GCC (4.2 and below AFAIK) aren't great about supporting
// -Wmissing-format-attribute
// when the function is anything more complex than foo(NSString *fmt, ...).
// You see the error inside the function when you turn ... into va_args and
// attempt to call another function (like vsprintf for example).
// So we just shut off the warning for this file. We reenable it at the end.
#pragma GCC diagnostic ignored "-Wmissing-format-attribute"
#endif // !__clang__
@interface GTMLogger (PrivateMethods)
- (void)logInternalFunc:(const char *)func
format:(NSString *)fmt
valist:(va_list)args
level:(GTMLoggerLevel)level;
@end
// Reference to the shared GTMLogger instance. This is not a singleton, it's
// Reference to the shared GTMLogger instance. This is not a singleton, it's
// just an easy reference to one shared instance.
static GTMLogger *gSharedLogger = nil;
@implementation GTMLogger
// Returns a pointer to the shared logger instance. If none exists, a standard
// Returns a pointer to the shared logger instance. If none exists, a standard
// logger is created and returned.
+ (id)sharedLogger {
@synchronized(self) {
if (gSharedLogger == nil) {
gSharedLogger = [[self standardLogger] retain];
}
GTMLOGGER_ASSERT(gSharedLogger != nil);
}
return [[gSharedLogger retain] autorelease];
}
@@ -69,24 +60,85 @@ static GTMLogger *gSharedLogger = nil;
}
+ (id)standardLogger {
id<GTMLogWriter> writer = [NSFileHandle fileHandleWithStandardOutput];
id<GTMLogFormatter> fr = [[[GTMLogStandardFormatter alloc] init] autorelease];
id<GTMLogFilter> filter = [[[GTMLogLevelFilter alloc] init] autorelease];
return [self loggerWithWriter:writer formatter:fr filter:filter];
// Don't trust NSFileHandle not to throw
@try {
id<GTMLogWriter> writer = [NSFileHandle fileHandleWithStandardOutput];
id<GTMLogFormatter> fr = [[[GTMLogStandardFormatter alloc] init]
autorelease];
id<GTMLogFilter> filter = [[[GTMLogLevelFilter alloc] init] autorelease];
return [[[self alloc] initWithWriter:writer
formatter:fr
filter:filter] autorelease];
}
@catch (id e) {
// Ignored
}
return nil;
}
+ (id)standardLoggerWithStderr {
id me = [self standardLogger];
[me setWriter:[NSFileHandle fileHandleWithStandardError]];
return me;
// Don't trust NSFileHandle not to throw
@try {
id me = [self standardLogger];
[me setWriter:[NSFileHandle fileHandleWithStandardError]];
return me;
}
@catch (id e) {
// Ignored
}
return nil;
}
+ (id)standardLoggerWithStdoutAndStderr {
// We're going to take advantage of the GTMLogger to GTMLogWriter adaptor
// and create a composite logger that an outer "standard" logger can use
// as a writer. Our inner loggers should apply no formatting since the main
// logger does that and we want the caller to be able to change formatters
// or add writers without knowing the inner structure of our composite.
// Don't trust NSFileHandle not to throw
@try {
GTMLogBasicFormatter *formatter = [[[GTMLogBasicFormatter alloc] init]
autorelease];
GTMLogger *stdoutLogger =
[self loggerWithWriter:[NSFileHandle fileHandleWithStandardOutput]
formatter:formatter
filter:[[[GTMLogMaximumLevelFilter alloc]
initWithMaximumLevel:kGTMLoggerLevelInfo]
autorelease]];
GTMLogger *stderrLogger =
[self loggerWithWriter:[NSFileHandle fileHandleWithStandardError]
formatter:formatter
filter:[[[GTMLogMininumLevelFilter alloc]
initWithMinimumLevel:kGTMLoggerLevelError]
autorelease]];
GTMLogger *compositeWriter =
[self loggerWithWriter:[NSArray arrayWithObjects:
stdoutLogger, stderrLogger, nil]
formatter:formatter
filter:[[[GTMLogNoFilter alloc] init] autorelease]];
GTMLogger *outerLogger = [self standardLogger];
[outerLogger setWriter:compositeWriter];
return outerLogger;
}
@catch (id e) {
// Ignored
}
return nil;
}
+ (id)standardLoggerWithPath:(NSString *)path {
NSFileHandle *fh = [NSFileHandle fileHandleForLoggingAtPath:path mode:0644];
if (fh == nil) return nil;
id me = [self standardLogger];
[me setWriter:fh];
return me;
@try {
NSFileHandle *fh = [NSFileHandle fileHandleForLoggingAtPath:path mode:0644];
if (fh == nil) return nil;
id me = [self standardLogger];
[me setWriter:fh];
return me;
}
@catch (id e) {
// Ignored
}
return nil;
}
+ (id)loggerWithWriter:(id<GTMLogWriter>)writer
@@ -112,69 +164,85 @@ static GTMLogger *gSharedLogger = nil;
[self setWriter:writer];
[self setFormatter:formatter];
[self setFilter:filter];
GTMLOGGER_ASSERT(formatter_ != nil);
GTMLOGGER_ASSERT(filter_ != nil);
GTMLOGGER_ASSERT(writer_ != nil);
}
return self;
}
- (void)dealloc {
GTMLOGGER_ASSERT(writer_ != nil);
GTMLOGGER_ASSERT(formatter_ != nil);
GTMLOGGER_ASSERT(filter_ != nil);
[writer_ release];
[formatter_ release];
[filter_ release];
// Unlikely, but |writer_| may be an NSFileHandle, which can throw
@try {
[formatter_ release];
[filter_ release];
[writer_ release];
}
@catch (id e) {
// Ignored
}
[super dealloc];
}
- (id<GTMLogWriter>)writer {
GTMLOGGER_ASSERT(writer_ != nil);
return [[writer_ retain] autorelease];
}
- (void)setWriter:(id<GTMLogWriter>)writer {
@synchronized(self) {
[writer_ autorelease];
if (writer == nil)
writer_ = [[NSFileHandle fileHandleWithStandardOutput] retain];
else
writer_ = nil;
if (writer == nil) {
// Try to use stdout, but don't trust NSFileHandle
@try {
writer_ = [[NSFileHandle fileHandleWithStandardOutput] retain];
}
@catch (id e) {
// Leave |writer_| nil
}
} else {
writer_ = [writer retain];
}
}
GTMLOGGER_ASSERT(writer_ != nil);
}
- (id<GTMLogFormatter>)formatter {
GTMLOGGER_ASSERT(formatter_ != nil);
return [[formatter_ retain] autorelease];
}
- (void)setFormatter:(id<GTMLogFormatter>)formatter {
@synchronized(self) {
[formatter_ autorelease];
if (formatter == nil)
formatter_ = [[GTMLogBasicFormatter alloc] init];
else
formatter_ = nil;
if (formatter == nil) {
@try {
formatter_ = [[GTMLogBasicFormatter alloc] init];
}
@catch (id e) {
// Leave |formatter_| nil
}
} else {
formatter_ = [formatter retain];
}
}
GTMLOGGER_ASSERT(formatter_ != nil);
}
- (id<GTMLogFilter>)filter {
GTMLOGGER_ASSERT(filter_ != nil);
return [[filter_ retain] autorelease];
}
- (void)setFilter:(id<GTMLogFilter>)filter {
@synchronized(self) {
[filter_ autorelease];
if (filter == nil)
filter_ = [[GTMLogNoFilter alloc] init];
else
filter_ = nil;
if (filter == nil) {
@try {
filter_ = [[GTMLogNoFilter alloc] init];
}
@catch (id e) {
// Leave |filter_| nil
}
} else {
filter_ = [filter retain];
}
}
GTMLOGGER_ASSERT(filter_ != nil);
}
- (void)logDebug:(NSString *)fmt, ... {
@@ -207,7 +275,6 @@ static GTMLogger *gSharedLogger = nil;
@end // GTMLogger
@implementation GTMLogger (GTMLoggerMacroHelpers)
- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... {
@@ -240,24 +307,26 @@ static GTMLogger *gSharedLogger = nil;
@end // GTMLoggerMacroHelpers
@implementation GTMLogger (PrivateMethods)
- (void)logInternalFunc:(const char *)func
format:(NSString *)fmt
valist:(va_list)args
valist:(va_list)args
level:(GTMLoggerLevel)level {
GTMLOGGER_ASSERT(formatter_ != nil);
GTMLOGGER_ASSERT(filter_ != nil);
GTMLOGGER_ASSERT(writer_ != nil);
NSString *fname = func ? [NSString stringWithUTF8String:func] : nil;
NSString *msg = [formatter_ stringForFunc:fname
withFormat:fmt
valist:args
level:level];
if (msg && [filter_ filterAllowsMessage:msg level:level])
[writer_ logMessage:msg level:level];
// Primary point where logging happens, logging should never throw, catch
// everything.
@try {
NSString *fname = func ? [NSString stringWithUTF8String:func] : nil;
NSString *msg = [formatter_ stringForFunc:fname
withFormat:fmt
valist:args
level:level];
if (msg && [filter_ filterAllowsMessage:msg level:level])
[writer_ logMessage:msg level:level];
}
@catch (id e) {
// Ignored
}
}
@end // PrivateMethods
@@ -278,8 +347,16 @@ static GTMLogger *gSharedLogger = nil;
- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
@synchronized(self) {
NSString *line = [NSString stringWithFormat:@"%@\n", msg];
[self writeData:[line dataUsingEncoding:NSUTF8StringEncoding]];
// Closed pipes should not generate exceptions in our caller. Catch here
// as well [GTMLogger logInternalFunc:...] so that an exception in this
// writer does not prevent other writers from having a chance.
@try {
NSString *line = [NSString stringWithFormat:@"%@\n", msg];
[self writeData:[line dataUsingEncoding:NSUTF8StringEncoding]];
}
@catch (id e) {
// Ignored
}
}
}
@@ -306,18 +383,18 @@ static GTMLogger *gSharedLogger = nil;
- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
switch (level) {
case kGTMLoggerLevelDebug:
[self logDebug:@"%@", msg];
[self logDebug:@"%@", msg];
break;
case kGTMLoggerLevelInfo:
[self logInfo:@"%@", msg];
break;
case kGTMLoggerLevelError:
case kGTMLoggerLevelError:
[self logError:@"%@", msg];
break;
case kGTMLoggerLevelAssert:
[self logAssert:@"%@", msg];
break;
default:
default:
// Ignore the message.
break;
}
@@ -328,19 +405,32 @@ static GTMLogger *gSharedLogger = nil;
@implementation GTMLogBasicFormatter
- (NSString *)prettyNameForFunc:(NSString *)func {
NSString *name = [func stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSString *function = @"(unknown)";
if ([name length]) {
if (// Objective C __func__ and __PRETTY_FUNCTION__
[name hasPrefix:@"-["] || [name hasPrefix:@"+["] ||
// C++ __PRETTY_FUNCTION__ and other preadorned formats
[name hasSuffix:@")"]) {
function = name;
} else {
// Assume C99 __func__
function = [NSString stringWithFormat:@"%@()", name];
}
}
return function;
}
- (NSString *)stringForFunc:(NSString *)func
withFormat:(NSString *)fmt
valist:(va_list)args
valist:(va_list)args
level:(GTMLoggerLevel)level {
// Performance note: since we always have to create a new NSString from the
// returned CFStringRef, we may want to do a quick check here to see if |fmt|
// contains a '%', and if not, simply return 'fmt'.
CFStringRef cfmsg = NULL;
cfmsg = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault,
NULL, // format options
(CFStringRef)fmt,
args);
return GTMCFAutorelease(cfmsg);
// Performance note: We may want to do a quick check here to see if |fmt|
// contains a '%', and if not, simply return 'fmt'.
if (!(fmt && args)) return nil;
return [[[NSString alloc] initWithFormat:fmt arguments:args] autorelease];
}
@end // GTMLogBasicFormatter
@@ -355,6 +445,10 @@ static GTMLogger *gSharedLogger = nil;
[dateFormatter_ setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"];
pname_ = [[[NSProcessInfo processInfo] processName] copy];
pid_ = [[NSProcessInfo processInfo] processIdentifier];
if (!(dateFormatter_ && pname_)) {
[self release];
return nil;
}
}
return self;
}
@@ -367,17 +461,17 @@ static GTMLogger *gSharedLogger = nil;
- (NSString *)stringForFunc:(NSString *)func
withFormat:(NSString *)fmt
valist:(va_list)args
valist:(va_list)args
level:(GTMLoggerLevel)level {
GTMLOGGER_ASSERT(dateFormatter_ != nil);
NSString *tstamp = nil;
@synchronized (dateFormatter_) {
tstamp = [dateFormatter_ stringFromDate:[NSDate date]];
}
return [NSString stringWithFormat:@"%@ %@[%d/%p] [lvl=%d] %@ %@",
tstamp, pname_, pid_, pthread_self(),
level, (func ? func : @"(no func)"),
[super stringForFunc:func withFormat:fmt valist:args level:level]];
tstamp, pname_, pid_, pthread_self(),
level, [self prettyNameForFunc:func],
// |super| has guard for nil |fmt| and |args|
[super stringForFunc:func withFormat:fmt valist:args level:level]];
}
@end // GTMLogStandardFormatter
@@ -391,14 +485,20 @@ static GTMLogger *gSharedLogger = nil;
// COV_NF_START
static BOOL IsVerboseLoggingEnabled(void) {
static NSString *const kVerboseLoggingKey = @"GTMVerboseLogging";
static char *env = NULL;
if (env == NULL)
env = getenv([kVerboseLoggingKey UTF8String]);
if (env && env[0]) {
return (strtol(env, NULL, 10) != 0);
NSString *value = [[[NSProcessInfo processInfo] environment]
objectForKey:kVerboseLoggingKey];
if (value) {
// Emulate [NSString boolValue] for pre-10.5
value = [value stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([[value uppercaseString] hasPrefix:@"Y"] ||
[[value uppercaseString] hasPrefix:@"T"] ||
[value intValue]) {
return YES;
} else {
return NO;
}
}
return [[NSUserDefaults standardUserDefaults] boolForKey:kVerboseLoggingKey];
}
// COV_NF_END
@@ -409,15 +509,15 @@ static BOOL IsVerboseLoggingEnabled(void) {
#if DEBUG
return YES;
#endif
BOOL allow = YES;
switch (level) {
case kGTMLoggerLevelDebug:
allow = NO;
break;
case kGTMLoggerLevelInfo:
allow = (IsVerboseLoggingEnabled() == YES);
allow = IsVerboseLoggingEnabled();
break;
case kGTMLoggerLevelError:
allow = YES;
@@ -443,3 +543,70 @@ static BOOL IsVerboseLoggingEnabled(void) {
}
@end // GTMLogNoFilter
@implementation GTMLogAllowedLevelFilter
// Private designated initializer
- (id)initWithAllowedLevels:(NSIndexSet *)levels {
self = [super init];
if (self != nil) {
allowedLevels_ = [levels retain];
// Cap min/max level
if (!allowedLevels_ ||
// NSIndexSet is unsigned so only check the high bound, but need to
// check both first and last index because NSIndexSet appears to allow
// wraparound.
([allowedLevels_ firstIndex] > kGTMLoggerLevelAssert) ||
([allowedLevels_ lastIndex] > kGTMLoggerLevelAssert)) {
[self release];
return nil;
}
}
return self;
}
- (id)init {
// Allow all levels in default init
return [self initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange:
NSMakeRange(kGTMLoggerLevelUnknown,
(kGTMLoggerLevelAssert - kGTMLoggerLevelUnknown + 1))]];
}
- (void)dealloc {
[allowedLevels_ release];
[super dealloc];
}
- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level {
return [allowedLevels_ containsIndex:level];
}
@end // GTMLogAllowedLevelFilter
@implementation GTMLogMininumLevelFilter
- (id)initWithMinimumLevel:(GTMLoggerLevel)level {
return [super initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange:
NSMakeRange(level,
(kGTMLoggerLevelAssert - level + 1))]];
}
@end // GTMLogMininumLevelFilter
@implementation GTMLogMaximumLevelFilter
- (id)initWithMaximumLevel:(GTMLoggerLevel)level {
return [super initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange:
NSMakeRange(kGTMLoggerLevelUnknown, level + 1)]];
}
@end // GTMLogMaximumLevelFilter
#if !defined(__clang__) && (__GNUC__*10+__GNUC_MINOR__ >= 42)
// See comment at top of file.
#pragma GCC diagnostic error "-Wmissing-format-attribute"
#endif // !__clang__

View File

@@ -164,11 +164,11 @@ class MachMessage {
public:
// The receiver of the message can retrieve the raw data this way
u_int8_t *GetData() {
uint8_t *GetData() {
return GetDataLength() > 0 ? GetDataPacket()->data : NULL;
}
u_int32_t GetDataLength() {
uint32_t GetDataLength() {
return EndianU32_LtoN(GetDataPacket()->data_length);
}
@@ -210,7 +210,7 @@ class MachMessage {
struct MessageDataPacket {
int32_t id; // little-endian
int32_t data_length; // little-endian
u_int8_t data[1]; // actual size limited by sizeof(MachMessage)
uint8_t data[1]; // actual size limited by sizeof(MachMessage)
};
MessageDataPacket* GetDataPacket();
@@ -223,7 +223,7 @@ class MachMessage {
mach_msg_header_t head;
mach_msg_body_t body;
u_int8_t padding[1024]; // descriptors and data may be embedded here
uint8_t padding[1024]; // descriptors and data may be embedded here
};
//==============================================================================

View File

@@ -1,195 +0,0 @@
// Copyright (c) 2007, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// SimpleStringDictionary.h
//
#ifndef SimpleStringDictionary_H__
#define SimpleStringDictionary_H__
#import <string>
#import <vector>
namespace google_breakpad {
//==============================================================================
// SimpleStringDictionary (and associated class KeyValueEntry) implement a very
// basic dictionary container class. It has the property of not making any
// memory allocations when getting and setting values. But it is not very
// efficient, with calls to get and set values operating in linear time.
// It has the additional limitation of having a fairly small fixed capacity of
// SimpleStringDictionary::MAX_NUM_ENTRIES entries. An assert() will fire if
// the client attempts to set more than this number of key/value pairs.
// Ordinarilly a C++ programmer would use something like the std::map template
// class, or on the Macintosh would often choose CFDictionary or NSDictionary.
// But these dictionary classes may call malloc() during get and set operations.
// Google Breakpad requires that no memory allocations be made in code running
// in its exception handling thread, so it uses SimpleStringDictionary as the
// underlying implementation for the GoogleBreakpad.framework APIs:
// GoogleBreakpadSetKeyValue(), GoogleBreakpadKeyValue(), and
// GoogleBreakpadRemoveKeyValue()
//
//==============================================================================
// KeyValueEntry
//
// A helper class used by SimpleStringDictionary representing a single
// storage cell for a key/value pair. Each key and value string are
// limited to MAX_STRING_STORAGE_SIZE-1 bytes (not glyphs). This class
// performs no memory allocations. It has methods for setting and getting
// key and value strings.
//
class KeyValueEntry {
public:
KeyValueEntry() {
Clear();
}
KeyValueEntry(const char *key, const char *value) {
SetKeyValue(key, value);
}
void SetKeyValue(const char *key, const char *value) {
if (!key) {
key = "";
}
if (!value) {
value = "";
}
strlcpy(key_, key, sizeof(key_));
strlcpy(value_, value, sizeof(value_));
}
void SetValue(const char *value) {
if (!value) {
value = "";
}
strlcpy(value_, value, sizeof(value_));
};
// Removes the key/value
void Clear() {
memset(key_, 0, sizeof(key_));
memset(value_, 0, sizeof(value_));
}
bool IsActive() const { return key_[0] != '\0'; }
const char *GetKey() const { return key_; }
const char *GetValue() const { return value_; }
// Don't change this without considering the fixed size
// of MachMessage (in MachIPC.h)
// (see also struct KeyValueMessageData in Inspector.h)
enum {MAX_STRING_STORAGE_SIZE = 256};
private:
char key_[MAX_STRING_STORAGE_SIZE];
char value_[MAX_STRING_STORAGE_SIZE];
};
//==============================================================================
// This class is not an efficient dictionary, but for the purposes of breakpad
// will be just fine. We're just dealing with ten or so distinct
// key/value pairs. The idea is to avoid any malloc() or free() calls
// in certain important methods to be called when a process is in a
// crashed state. Each key and value string are limited to
// KeyValueEntry::MAX_STRING_STORAGE_SIZE-1 bytes (not glyphs). Strings passed
// in exceeding this length will be truncated.
//
class SimpleStringDictionary {
public:
SimpleStringDictionary() {}; // entries will all be cleared
// Returns the number of active key/value pairs. The upper limit for this
// is MAX_NUM_ENTRIES.
int GetCount() const;
// Given |key|, returns its corresponding |value|.
// If |key| is NULL, an assert will fire or NULL will be returned. If |key|
// is not found or is an empty string, NULL is returned.
const char *GetValueForKey(const char *key) const;
// Stores a string |value| represented by |key|. If |key| is NULL or an empty
// string, this will assert (or do nothing). If |value| is NULL then
// the |key| will be removed. An empty string is OK for |value|.
void SetKeyValue(const char *key, const char *value);
// Given |key|, removes any associated value. It will assert (or do nothing)
// if NULL is passed in. It will do nothing if |key| is not found.
void RemoveKey(const char *key);
// This is the maximum number of key/value pairs which may be set in the
// dictionary. An assert may fire if more values than this are set.
// Don't change this without also changing comment in GoogleBreakpad.h
enum {MAX_NUM_ENTRIES = 64};
private:
friend class SimpleStringDictionaryIterator;
const KeyValueEntry *GetEntry(int i) const;
KeyValueEntry entries_[MAX_NUM_ENTRIES];
};
//==============================================================================
class SimpleStringDictionaryIterator {
public:
SimpleStringDictionaryIterator(const SimpleStringDictionary &dict)
: dict_(dict), i_(0) {
}
// Initializes iterator to the beginning (may later call Next() )
void Start() {
i_ = 0;
}
// like the nextObject method of NSEnumerator (in Cocoa)
// returns NULL when there are no more entries
//
const KeyValueEntry* Next() {
for (; i_ < SimpleStringDictionary::MAX_NUM_ENTRIES; ++i_) {
const KeyValueEntry *entry = dict_.GetEntry(i_);
if (entry->IsActive()) {
i_++; // move to next entry for next time
return entry;
}
}
return NULL; // reached end of array
}
private:
const SimpleStringDictionary& dict_;
int i_;
};
} // namespace google_breakpad
#endif // SimpleStringDictionary_H__

View File

@@ -1,133 +0,0 @@
// Copyright (c) 2007, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// SimpleStringDictionary.mm
// Simple string dictionary that does not allocate memory
//
#include <assert.h>
#import "SimpleStringDictionary.h"
namespace google_breakpad {
//==============================================================================
const KeyValueEntry *SimpleStringDictionary::GetEntry(int i) const {
return (i >= 0 && i < MAX_NUM_ENTRIES) ? &entries_[i] : NULL;
}
//==============================================================================
int SimpleStringDictionary::GetCount() const {
int count = 0;
for (int i = 0; i < MAX_NUM_ENTRIES; ++i) {
if (entries_[i].IsActive() ) {
++count;
}
}
return count;
}
//==============================================================================
const char *SimpleStringDictionary::GetValueForKey(const char *key) const {
assert(key);
if (!key)
return NULL;
for (int i = 0; i < MAX_NUM_ENTRIES; ++i) {
const KeyValueEntry &entry = entries_[i];
if (entry.IsActive() && !strcmp(entry.GetKey(), key)) {
return entry.GetValue();
}
}
return NULL;
}
//==============================================================================
void SimpleStringDictionary::SetKeyValue(const char *key,
const char *value) {
if (!value) {
RemoveKey(key);
return;
}
// key must not be NULL
assert(key);
if (!key)
return;
// key must not be empty string
assert(key[0] != '\0');
if (key[0] == '\0')
return;
int free_index = -1;
// check if key already exists
for (int i = 0; i < MAX_NUM_ENTRIES; ++i) {
KeyValueEntry &entry = entries_[i];
if (entry.IsActive()) {
if (!strcmp(entry.GetKey(), key)) {
entry.SetValue(value);
return;
}
} else {
// Make a note of an empty slot
if (free_index == -1) {
free_index = i;
}
}
}
// check if we've run out of space
assert(free_index != -1);
// Put new key into an empty slot (if found)
if (free_index != -1) {
entries_[free_index].SetKeyValue(key, value);
}
}
//==============================================================================
void SimpleStringDictionary::RemoveKey(const char *key) {
assert(key);
if (!key)
return;
for (int i = 0; i < MAX_NUM_ENTRIES; ++i) {
if (!strcmp(entries_[i].GetKey(), key)) {
entries_[i].Clear();
return;
}
}
}
} // namespace google_breakpad

View File

@@ -0,0 +1,113 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "common/mac/arch_utilities.h"
#include <mach-o/arch.h>
#include <stdio.h>
#include <string.h>
#ifndef CPU_TYPE_ARM
#define CPU_TYPE_ARM (static_cast<cpu_type_t>(12))
#endif // CPU_TYPE_ARM
#ifndef CPU_SUBTYPE_ARM_V7
#define CPU_SUBTYPE_ARM_V7 (static_cast<cpu_subtype_t>(9))
#endif // CPU_SUBTYPE_ARM_V7
#ifndef CPU_SUBTYPE_ARM_V7S
#define CPU_SUBTYPE_ARM_V7S (static_cast<cpu_subtype_t>(11))
#endif // CPU_SUBTYPE_ARM_V7S
#ifndef CPU_TYPE_ARM64
#define CPU_TYPE_ARM64 (static_cast<cpu_type_t>(16777228))
#endif // CPU_TYPE_ARM64
#ifndef CPU_SUBTYPE_ARM64_ALL
#define CPU_SUBTYPE_ARM64_ALL (static_cast<cpu_type_t>(0))
#endif // CPU_SUBTYPE_ARM64_ALL
namespace {
const NXArchInfo* ArchInfo_arm64() {
NXArchInfo* arm64 = new NXArchInfo;
*arm64 = *NXGetArchInfoFromCpuType(CPU_TYPE_ARM,
CPU_SUBTYPE_ARM_V7);
arm64->name = "arm64";
arm64->cputype = CPU_TYPE_ARM64;
arm64->cpusubtype = CPU_SUBTYPE_ARM64_ALL;
arm64->description = "arm 64";
return arm64;
}
const NXArchInfo* ArchInfo_armv7s() {
NXArchInfo* armv7s = new NXArchInfo;
*armv7s = *NXGetArchInfoFromCpuType(CPU_TYPE_ARM,
CPU_SUBTYPE_ARM_V7);
armv7s->name = "armv7s";
armv7s->cpusubtype = CPU_SUBTYPE_ARM_V7S;
armv7s->description = "arm v7s";
return armv7s;
}
} // namespace
namespace google_breakpad {
const NXArchInfo* BreakpadGetArchInfoFromName(const char* arch_name) {
// TODO: Remove this when the OS knows about arm64.
if (!strcmp("arm64", arch_name))
return BreakpadGetArchInfoFromCpuType(CPU_TYPE_ARM64,
CPU_SUBTYPE_ARM64_ALL);
// TODO: Remove this when the OS knows about armv7s.
if (!strcmp("armv7s", arch_name))
return BreakpadGetArchInfoFromCpuType(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S);
return NXGetArchInfoFromName(arch_name);
}
const NXArchInfo* BreakpadGetArchInfoFromCpuType(cpu_type_t cpu_type,
cpu_subtype_t cpu_subtype) {
// TODO: Remove this when the OS knows about arm64.
if (cpu_type == CPU_TYPE_ARM64 && cpu_subtype == CPU_SUBTYPE_ARM64_ALL) {
static const NXArchInfo* arm64 = ArchInfo_arm64();
return arm64;
}
// TODO: Remove this when the OS knows about armv7s.
if (cpu_type == CPU_TYPE_ARM && cpu_subtype == CPU_SUBTYPE_ARM_V7S) {
static const NXArchInfo* armv7s = ArchInfo_armv7s();
return armv7s;
}
return NXGetArchInfoFromCpuType(cpu_type, cpu_subtype);
}
} // namespace google_breakpad

View File

@@ -0,0 +1,47 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// arch_utilities.h: Utilities for architecture introspection for Mac platform.
#ifndef COMMON_MAC_ARCH_UTILITIES_H__
#define COMMON_MAC_ARCH_UTILITIES_H__
#include <mach-o/arch.h>
namespace google_breakpad {
// Custom implementation of |NXGetArchInfoFromName| and
// |NXGetArchInfoFromCpuType| that handle newer CPU on older OSes.
const NXArchInfo* BreakpadGetArchInfoFromName(const char* arch_name);
const NXArchInfo* BreakpadGetArchInfoFromCpuType(cpu_type_t cpu_type,
cpu_subtype_t cpu_subtype);
} // namespace google_breakpad
#endif // COMMON_MAC_ARCH_UTILITIES_H__

View File

@@ -47,13 +47,16 @@
#include "common/byte_cursor.h"
#include "common/mac/macho_reader.h"
#include "common/module.h"
#include "common/symbol_data.h"
namespace google_breakpad {
class DumpSymbols {
public:
DumpSymbols()
: input_pathname_(),
DumpSymbols(SymbolData symbol_data, bool handle_inter_cu_refs)
: symbol_data_(symbol_data),
handle_inter_cu_refs_(handle_inter_cu_refs),
input_pathname_(),
object_filename_(),
contents_(),
selected_object_file_(),
@@ -110,9 +113,14 @@ class DumpSymbols {
}
// Read the selected object file's debugging information, and write it out to
// |stream|. Write the CFI section if |cfi| is true. Return true on success;
// if an error occurs, report it and return false.
bool WriteSymbolFile(std::ostream &stream, bool cfi);
// |stream|. Return true on success; if an error occurs, report it and
// return false.
bool WriteSymbolFile(std::ostream &stream);
// As above, but simply return the debugging information in module
// instead of writing it to a stream. The caller owns the resulting
// module object and must delete it when finished.
bool ReadSymbolData(Module** module);
private:
// Used internally.
@@ -127,7 +135,8 @@ class DumpSymbols {
// on failure, report the problem and return false.
bool ReadDwarf(google_breakpad::Module *module,
const mach_o::Reader &macho_reader,
const mach_o::SectionMap &dwarf_sections) const;
const mach_o::SectionMap &dwarf_sections,
bool handle_inter_cu_refs) const;
// Read DWARF CFI or .eh_frame data from |section|, belonging to
// |macho_reader|, and record it in |module|. If |eh_frame| is true,
@@ -139,6 +148,12 @@ class DumpSymbols {
const mach_o::Section &section,
bool eh_frame) const;
// The selection of what type of symbol data to read/write.
const SymbolData symbol_data_;
// Whether to handle references between compilation units.
const bool handle_inter_cu_refs_;
// The name of the file or bundle whose symbols this will dump.
// This is the path given to Read, for use in error messages.
NSString *input_pathname_;

View File

@@ -50,10 +50,13 @@
#include "common/dwarf_cu_to_module.h"
#include "common/dwarf_line_to_module.h"
#include "common/mac/file_id.h"
#include "common/mac/arch_utilities.h"
#include "common/mac/macho_reader.h"
#include "common/module.h"
#include "common/scoped_ptr.h"
#include "common/stabs_reader.h"
#include "common/stabs_to_module.h"
#include "common/symbol_data.h"
#ifndef CPU_TYPE_ARM
#define CPU_TYPE_ARM (static_cast<cpu_type_t>(12))
@@ -69,6 +72,7 @@ using google_breakpad::mach_o::Segment;
using google_breakpad::Module;
using google_breakpad::StabsReader;
using google_breakpad::StabsToModule;
using google_breakpad::scoped_ptr;
using std::make_pair;
using std::pair;
using std::string;
@@ -191,7 +195,8 @@ bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type,
bool DumpSymbols::SetArchitecture(const std::string &arch_name) {
bool arch_set = false;
const NXArchInfo *arch_info = NXGetArchInfoFromName(arch_name.c_str());
const NXArchInfo *arch_info =
google_breakpad::BreakpadGetArchInfoFromName(arch_name.c_str());
if (arch_info) {
arch_set = SetArchitecture(arch_info->cputype, arch_info->cpusubtype);
}
@@ -202,7 +207,8 @@ string DumpSymbols::Identifier() {
FileID file_id([object_filename_ fileSystemRepresentation]);
unsigned char identifier_bytes[16];
cpu_type_t cpu_type = selected_object_file_->cputype;
if (!file_id.MachoIdentifier(cpu_type, identifier_bytes)) {
cpu_subtype_t cpu_subtype = selected_object_file_->cpusubtype;
if (!file_id.MachoIdentifier(cpu_type, cpu_subtype, identifier_bytes)) {
fprintf(stderr, "Unable to calculate UUID of mach-o binary %s!\n",
[object_filename_ fileSystemRepresentation]);
return "";
@@ -224,24 +230,31 @@ string DumpSymbols::Identifier() {
// dwarf2reader::LineInfo and populates a Module and a line vector
// with the results.
class DumpSymbols::DumperLineToModule:
public DwarfCUToModule::LineToModuleFunctor {
public DwarfCUToModule::LineToModuleHandler {
public:
// Create a line-to-module converter using BYTE_READER.
DumperLineToModule(dwarf2reader::ByteReader *byte_reader)
: byte_reader_(byte_reader) { }
void operator()(const char *program, uint64 length,
Module *module, vector<Module::Line> *lines) {
DwarfLineToModule handler(module, lines);
void StartCompilationUnit(const string& compilation_dir) {
compilation_dir_ = compilation_dir;
}
void ReadProgram(const char *program, uint64 length,
Module *module, vector<Module::Line> *lines) {
DwarfLineToModule handler(module, compilation_dir_, lines);
dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler);
parser.Start();
}
private:
string compilation_dir_;
dwarf2reader::ByteReader *byte_reader_; // WEAK
};
bool DumpSymbols::ReadDwarf(google_breakpad::Module *module,
const mach_o::Reader &macho_reader,
const mach_o::SectionMap &dwarf_sections) const {
const mach_o::SectionMap &dwarf_sections,
bool handle_inter_cu_refs) const {
// Build a byte reader of the appropriate endianness.
ByteReader byte_reader(macho_reader.big_endian()
? dwarf2reader::ENDIANNESS_BIG
@@ -249,19 +262,24 @@ bool DumpSymbols::ReadDwarf(google_breakpad::Module *module,
// Construct a context for this file.
DwarfCUToModule::FileContext file_context(selected_object_name_,
module);
module,
handle_inter_cu_refs);
// Build a dwarf2reader::SectionMap from our mach_o::SectionMap.
for (mach_o::SectionMap::const_iterator it = dwarf_sections.begin();
it != dwarf_sections.end(); it++) {
file_context.section_map[it->first] =
make_pair(reinterpret_cast<const char *>(it->second.contents.start),
it->second.contents.Size());
it != dwarf_sections.end(); ++it) {
file_context.AddSectionToSectionMap(
it->first,
reinterpret_cast<const char *>(it->second.contents.start),
it->second.contents.Size());
}
// Find the __debug_info section.
std::pair<const char *, uint64> debug_info_section
= file_context.section_map["__debug_info"];
dwarf2reader::SectionMap::const_iterator debug_info_entry =
file_context.section_map().find("__debug_info");
assert(debug_info_entry != file_context.section_map().end());
const std::pair<const char*, uint64>& debug_info_section =
debug_info_entry->second;
// There had better be a __debug_info section!
if (!debug_info_section.first) {
fprintf(stderr, "%s: __DWARF segment of file has no __debug_info section\n",
@@ -283,7 +301,7 @@ bool DumpSymbols::ReadDwarf(google_breakpad::Module *module,
// Make a Dwarf2Handler that drives our DIEHandler.
dwarf2reader::DIEDispatcher die_dispatcher(&root_handler);
// Make a DWARF parser for the compilation unit at OFFSET.
dwarf2reader::CompilationUnit dwarf_reader(file_context.section_map,
dwarf2reader::CompilationUnit dwarf_reader(file_context.section_map(),
offset,
&byte_reader,
&die_dispatcher);
@@ -312,9 +330,8 @@ bool DumpSymbols::ReadCFI(google_breakpad::Module *module,
register_names = DwarfCFIToModule::RegisterNames::ARM();
break;
default: {
const NXArchInfo *arch =
NXGetArchInfoFromCpuType(macho_reader.cpu_type(),
macho_reader.cpu_subtype());
const NXArchInfo *arch = google_breakpad::BreakpadGetArchInfoFromCpuType(
macho_reader.cpu_type(), macho_reader.cpu_subtype());
fprintf(stderr, "%s: cannot convert DWARF call frame information for ",
selected_object_name_.c_str());
if (arch)
@@ -362,8 +379,14 @@ class DumpSymbols::LoadCommandDumper:
// file, and adding data to MODULE.
LoadCommandDumper(const DumpSymbols &dumper,
google_breakpad::Module *module,
const mach_o::Reader &reader)
: dumper_(dumper), module_(module), reader_(reader) { }
const mach_o::Reader &reader,
SymbolData symbol_data,
bool handle_inter_cu_refs)
: dumper_(dumper),
module_(module),
reader_(reader),
symbol_data_(symbol_data),
handle_inter_cu_refs_(handle_inter_cu_refs) { }
bool SegmentCommand(const mach_o::Segment &segment);
bool SymtabCommand(const ByteBuffer &entries, const ByteBuffer &strings);
@@ -372,6 +395,8 @@ class DumpSymbols::LoadCommandDumper:
const DumpSymbols &dumper_;
google_breakpad::Module *module_; // WEAK
const mach_o::Reader &reader_;
const SymbolData symbol_data_;
const bool handle_inter_cu_refs_;
};
bool DumpSymbols::LoadCommandDumper::SegmentCommand(const Segment &segment) {
@@ -381,23 +406,31 @@ bool DumpSymbols::LoadCommandDumper::SegmentCommand(const Segment &segment) {
if (segment.name == "__TEXT") {
module_->SetLoadAddress(segment.vmaddr);
mach_o::SectionMap::const_iterator eh_frame =
section_map.find("__eh_frame");
if (eh_frame != section_map.end()) {
// If there is a problem reading this, don't treat it as a fatal error.
dumper_.ReadCFI(module_, reader_, eh_frame->second, true);
if (symbol_data_ != NO_CFI) {
mach_o::SectionMap::const_iterator eh_frame =
section_map.find("__eh_frame");
if (eh_frame != section_map.end()) {
// If there is a problem reading this, don't treat it as a fatal error.
dumper_.ReadCFI(module_, reader_, eh_frame->second, true);
}
}
return true;
}
if (segment.name == "__DWARF") {
if (!dumper_.ReadDwarf(module_, reader_, section_map))
return false;
mach_o::SectionMap::const_iterator debug_frame
= section_map.find("__debug_frame");
if (debug_frame != section_map.end()) {
// If there is a problem reading this, don't treat it as a fatal error.
dumper_.ReadCFI(module_, reader_, debug_frame->second, false);
if (symbol_data_ != ONLY_CFI) {
if (!dumper_.ReadDwarf(module_, reader_, section_map,
handle_inter_cu_refs_)) {
return false;
}
}
if (symbol_data_ != NO_CFI) {
mach_o::SectionMap::const_iterator debug_frame
= section_map.find("__debug_frame");
if (debug_frame != section_map.end()) {
// If there is a problem reading this, don't treat it as a fatal error.
dumper_.ReadCFI(module_, reader_, debug_frame->second, false);
}
}
}
@@ -421,7 +454,7 @@ bool DumpSymbols::LoadCommandDumper::SymtabCommand(const ByteBuffer &entries,
return true;
}
bool DumpSymbols::WriteSymbolFile(std::ostream &stream, bool cfi) {
bool DumpSymbols::ReadSymbolData(Module** out_module) {
// Select an object file, if SetArchitecture hasn't been called to set one
// explicitly.
if (!selected_object_file_) {
@@ -446,9 +479,9 @@ bool DumpSymbols::WriteSymbolFile(std::ostream &stream, bool cfi) {
// Find the name of the selected file's architecture, to appear in
// the MODULE record and in error messages.
const NXArchInfo *selected_arch_info
= NXGetArchInfoFromCpuType(selected_object_file_->cputype,
selected_object_file_->cpusubtype);
const NXArchInfo *selected_arch_info =
google_breakpad::BreakpadGetArchInfoFromCpuType(
selected_object_file_->cputype, selected_object_file_->cpusubtype);
const char *selected_arch_name = selected_arch_info->name;
if (strcmp(selected_arch_name, "i386") == 0)
@@ -472,8 +505,10 @@ bool DumpSymbols::WriteSymbolFile(std::ostream &stream, bool cfi) {
identifier += "0";
// Create a module to hold the debugging information.
Module module([module_name UTF8String], "mac", selected_arch_name,
identifier);
scoped_ptr<Module> module(new Module([module_name UTF8String],
"mac",
selected_arch_name,
identifier));
// Parse the selected object file.
mach_o::Reader::Reporter reporter(selected_object_name_);
@@ -486,11 +521,26 @@ bool DumpSymbols::WriteSymbolFile(std::ostream &stream, bool cfi) {
return false;
// Walk its load commands, and deal with whatever is there.
LoadCommandDumper load_command_dumper(*this, &module, reader);
LoadCommandDumper load_command_dumper(*this, module.get(), reader,
symbol_data_, handle_inter_cu_refs_);
if (!reader.WalkLoadCommands(&load_command_dumper))
return false;
return module.Write(stream, cfi);
*out_module = module.release();
return true;
}
bool DumpSymbols::WriteSymbolFile(std::ostream &stream) {
Module* module = NULL;
if (ReadSymbolData(&module) && module) {
bool res = module->Write(stream, symbol_data_);
delete module;
return res;
}
return false;
}
} // namespace google_breakpad

View File

@@ -70,13 +70,15 @@ bool FileID::FileIdentifier(unsigned char identifier[16]) {
return true;
}
bool FileID::MachoIdentifier(int cpu_type, unsigned char identifier[16]) {
bool FileID::MachoIdentifier(cpu_type_t cpu_type,
cpu_subtype_t cpu_subtype,
unsigned char identifier[16]) {
MachoID macho(path_);
if (macho.UUIDCommand(cpu_type, identifier))
if (macho.UUIDCommand(cpu_type, cpu_subtype, identifier))
return true;
return macho.MD5(cpu_type, identifier);
return macho.MD5(cpu_type, cpu_subtype, identifier);
}
// static
@@ -90,8 +92,10 @@ void FileID::ConvertIdentifierToString(const unsigned char identifier[16],
if (idx == 4 || idx == 6 || idx == 8 || idx == 10)
buffer[buffer_idx++] = '-';
buffer[buffer_idx++] = (hi >= 10) ? 'A' + hi - 10 : '0' + hi;
buffer[buffer_idx++] = (lo >= 10) ? 'A' + lo - 10 : '0' + lo;
buffer[buffer_idx++] =
static_cast<char>((hi >= 10) ? ('A' + hi - 10) : ('0' + hi));
buffer[buffer_idx++] =
static_cast<char>((lo >= 10) ? ('A' + lo - 10) : ('0' + lo));
}
// NULL terminate

View File

@@ -35,6 +35,7 @@
#define COMMON_MAC_FILE_ID_H__
#include <limits.h>
#include <mach/machine.h>
namespace google_breakpad {
@@ -50,15 +51,18 @@ class FileID {
bool FileIdentifier(unsigned char identifier[16]);
// Treat the file as a mach-o file that will contain one or more archicture.
// Accepted values for |cpu_type| (e.g., CPU_TYPE_X86 or CPU_TYPE_POWERPC)
// are listed in /usr/include/mach/machine.h.
// If |cpu_type| is 0, then the native cpu type is used.
// Returns false if opening the file failed or if the |cpu_type| is not
// present in the file.
// Accepted values for |cpu_type| and |cpu_subtype| (e.g., CPU_TYPE_X86 or
// CPU_TYPE_POWERPC) are listed in /usr/include/mach/machine.h.
// If |cpu_type| is 0, then the native cpu type is used. If |cpu_subtype| is
// CPU_SUBTYPE_MULTIPLE, the match is only done on |cpu_type|.
// Returns false if opening the file failed or if the |cpu_type|/|cpu_subtype|
// is not present in the file.
// Return the unique identifier in |identifier|.
// The current implementation will look for the (in order of priority):
// LC_UUID, LC_ID_DYLIB, or MD5 hash of the given |cpu_type|.
bool MachoIdentifier(int cpu_type, unsigned char identifier[16]);
bool MachoIdentifier(cpu_type_t cpu_type,
cpu_subtype_t cpu_subtype,
unsigned char identifier[16]);
// Convert the |identifier| data to a NULL terminated string. The string will
// be formatted as a UUID (e.g., 22F065BB-FC9C-49F7-80FE-26A7CEBD7BCE).
@@ -75,4 +79,3 @@ class FileID {
} // namespace google_breakpad
#endif // COMMON_MAC_FILE_ID_H__

View File

@@ -153,10 +153,12 @@ void MachoID::Update(MachoWalker *walker, off_t offset, size_t size) {
}
}
bool MachoID::UUIDCommand(int cpu_type, unsigned char bytes[16]) {
bool MachoID::UUIDCommand(cpu_type_t cpu_type,
cpu_subtype_t cpu_subtype,
unsigned char bytes[16]) {
struct breakpad_uuid_command uuid_cmd;
uuid_cmd.cmd = 0;
if (!WalkHeader(cpu_type, UUIDWalkerCB, &uuid_cmd))
if (!WalkHeader(cpu_type, cpu_subtype, UUIDWalkerCB, &uuid_cmd))
return false;
// If we found the command, we'll have initialized the uuid_command
@@ -169,10 +171,12 @@ bool MachoID::UUIDCommand(int cpu_type, unsigned char bytes[16]) {
return false;
}
bool MachoID::IDCommand(int cpu_type, unsigned char identifier[16]) {
bool MachoID::IDCommand(cpu_type_t cpu_type,
cpu_subtype_t cpu_subtype,
unsigned char identifier[16]) {
struct dylib_command dylib_cmd;
dylib_cmd.cmd = 0;
if (!WalkHeader(cpu_type, IDWalkerCB, &dylib_cmd))
if (!WalkHeader(cpu_type, cpu_subtype, IDWalkerCB, &dylib_cmd))
return false;
// If we found the command, we'll have initialized the dylib_command
@@ -210,37 +214,38 @@ bool MachoID::IDCommand(int cpu_type, unsigned char identifier[16]) {
return false;
}
uint32_t MachoID::Adler32(int cpu_type) {
uint32_t MachoID::Adler32(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) {
update_function_ = &MachoID::UpdateCRC;
crc_ = 0;
if (!WalkHeader(cpu_type, WalkerCB, this))
if (!WalkHeader(cpu_type, cpu_subtype, WalkerCB, this))
return 0;
return crc_;
}
bool MachoID::MD5(int cpu_type, unsigned char identifier[16]) {
bool MachoID::MD5(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype, unsigned char identifier[16]) {
update_function_ = &MachoID::UpdateMD5;
MD5Init(&md5_context_);
if (!WalkHeader(cpu_type, WalkerCB, this))
if (!WalkHeader(cpu_type, cpu_subtype, WalkerCB, this))
return false;
MD5Final(identifier, &md5_context_);
return true;
}
bool MachoID::WalkHeader(int cpu_type,
bool MachoID::WalkHeader(cpu_type_t cpu_type,
cpu_subtype_t cpu_subtype,
MachoWalker::LoadCommandCallback callback,
void *context) {
if (memory_) {
MachoWalker walker(memory_, memory_size_, callback, context);
return walker.WalkHeader(cpu_type);
return walker.WalkHeader(cpu_type, cpu_subtype);
} else {
MachoWalker walker(path_, callback, context);
return walker.WalkHeader(cpu_type);
return walker.WalkHeader(cpu_type, cpu_subtype);
}
}

View File

@@ -35,6 +35,7 @@
#define COMMON_MAC_MACHO_ID_H__
#include <limits.h>
#include <mach/machine.h>
#include <mach-o/loader.h>
#include "common/mac/macho_walker.h"
@@ -48,22 +49,32 @@ class MachoID {
MachoID(const char *path, void *memory, size_t size);
~MachoID();
// For the given |cpu_type|, return a UUID from the LC_UUID command.
// For the given |cpu_type| and |cpu_subtype|, return a UUID from the LC_UUID
// command.
// Return false if there isn't a LC_UUID command.
bool UUIDCommand(int cpu_type, unsigned char identifier[16]);
bool UUIDCommand(cpu_type_t cpu_type,
cpu_subtype_t cpu_subtype,
unsigned char identifier[16]);
// For the given |cpu_type|, return a UUID from the LC_ID_DYLIB command.
// For the given |cpu_type| and |cpu_subtype|, return a UUID from the
// LC_ID_DYLIB command.
// Return false if there isn't a LC_ID_DYLIB command.
bool IDCommand(int cpu_type, unsigned char identifier[16]);
bool IDCommand(cpu_type_t cpu_type,
cpu_subtype_t cpu_subtype,
unsigned char identifier[16]);
// For the given |cpu_type|, return the Adler32 CRC for the mach-o data
// segment(s).
// For the given |cpu_type| and |cpu_subtype|, return the Adler32 CRC for the
// mach-o data segment(s).
// Return 0 on error (e.g., if the file is not a mach-o file)
uint32_t Adler32(int cpu_type);
uint32_t Adler32(cpu_type_t cpu_type,
cpu_subtype_t cpu_subtype);
// For the given |cpu_type|, return the MD5 for the mach-o data segment(s).
// For the given |cpu_type|, and |cpu_subtype| return the MD5 for the mach-o
// data segment(s).
// Return true on success, false otherwise
bool MD5(int cpu_type, unsigned char identifier[16]);
bool MD5(cpu_type_t cpu_type,
cpu_subtype_t cpu_subtype,
unsigned char identifier[16]);
private:
// Signature of class member function to be called with data read from file
@@ -81,8 +92,8 @@ class MachoID {
void Update(MachoWalker *walker, off_t offset, size_t size);
// Factory for the MachoWalker
bool WalkHeader(int cpu_type, MachoWalker::LoadCommandCallback callback,
void *context);
bool WalkHeader(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype,
MachoWalker::LoadCommandCallback callback, void *context);
// The callback from the MachoWalker for CRC and MD5
static bool WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,

View File

@@ -43,6 +43,10 @@
#define CPU_TYPE_ARM 12
#endif
#if !defined(CPU_TYPE_ARM_64)
#define CPU_TYPE_ARM_64 16777228
#endif
namespace google_breakpad {
namespace mach_o {
@@ -242,6 +246,7 @@ bool Reader::Read(const uint8_t *buffer,
case CPU_TYPE_POWERPC:
expected_magic = MH_MAGIC;
break;
case CPU_TYPE_ARM_64:
case CPU_TYPE_X86_64:
expected_magic = MH_CIGAM_64;
break;

View File

@@ -54,14 +54,6 @@
# define LC_UUID 0x1b /* the uuid */
#endif
#if TARGET_CPU_X86
# define BREAKPAD_MACHINE_THREAD_STATE i386_THREAD_STATE
#elif TARGET_CPU_X86_64
# define BREAKPAD_MACHINE_THREAD_STATE x86_THREAD_STATE64
#else
# define BREAKPAD_MACHINE_THREAD_STATE MACHINE_THREAD_STATE
#endif
// The uuid_command struct/swap routines were added during the 10.4 series.
// Their presence isn't guaranteed.
struct breakpad_uuid_command {

View File

@@ -79,21 +79,18 @@ MachoWalker::~MachoWalker() {
close(file_);
}
int MachoWalker::ValidateCPUType(int cpu_type) {
// If the user didn't specify, use the local architecture.
bool MachoWalker::WalkHeader(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) {
cpu_type_t valid_cpu_type = cpu_type;
cpu_subtype_t valid_cpu_subtype = cpu_subtype;
// if |cpu_type| is 0, use the native cpu type.
if (cpu_type == 0) {
const NXArchInfo *arch = NXGetLocalArchInfo();
assert(arch);
cpu_type = arch->cputype;
valid_cpu_type = arch->cputype;
valid_cpu_subtype = CPU_SUBTYPE_MULTIPLE;
}
return cpu_type;
}
bool MachoWalker::WalkHeader(int cpu_type) {
int valid_cpu_type = ValidateCPUType(cpu_type);
off_t offset;
if (FindHeader(valid_cpu_type, offset)) {
if (FindHeader(valid_cpu_type, valid_cpu_subtype, offset)) {
if (cpu_type & CPU_ARCH_ABI64)
return WalkHeader64AtOffset(offset);
@@ -111,7 +108,7 @@ bool MachoWalker::ReadBytes(void *buffer, size_t size, off_t offset) {
if (offset + size > memory_size_) {
if (static_cast<size_t>(offset) >= memory_size_)
return false;
size = memory_size_ - offset;
size = memory_size_ - static_cast<size_t>(offset);
result = false;
}
memcpy(buffer, static_cast<char *>(memory_) + offset, size);
@@ -131,8 +128,9 @@ bool MachoWalker::CurrentHeader(struct mach_header_64 *header, off_t *offset) {
return false;
}
bool MachoWalker::FindHeader(int cpu_type, off_t &offset) {
int valid_cpu_type = ValidateCPUType(cpu_type);
bool MachoWalker::FindHeader(cpu_type_t cpu_type,
cpu_subtype_t cpu_subtype,
off_t &offset) {
// Read the magic bytes that's common amongst all mach-o files
uint32_t magic;
if (!ReadBytes(&magic, sizeof(magic), 0))
@@ -153,15 +151,18 @@ bool MachoWalker::FindHeader(int cpu_type, off_t &offset) {
if (!is_fat) {
// If we don't have a fat header, check if the cpu type matches the single
// header
cpu_type_t header_cpu_type;
if (!ReadBytes(&header_cpu_type, sizeof(header_cpu_type), offset))
struct mach_header header;
if (!ReadBytes(&header, sizeof(header), 0))
return false;
if (magic == MH_CIGAM || magic == MH_CIGAM_64)
header_cpu_type = ByteSwap(header_cpu_type);
swap_mach_header(&header, NXHostByteOrder());
if (valid_cpu_type != header_cpu_type)
if (cpu_type != header.cputype ||
(cpu_subtype != CPU_SUBTYPE_MULTIPLE &&
cpu_subtype != header.cpusubtype)) {
return false;
}
offset = 0;
return true;
@@ -186,7 +187,9 @@ bool MachoWalker::FindHeader(int cpu_type, off_t &offset) {
if (NXHostByteOrder() != NX_BigEndian)
swap_fat_arch(&arch, 1, NXHostByteOrder());
if (arch.cputype == valid_cpu_type) {
if (arch.cputype == cpu_type &&
(cpu_subtype == CPU_SUBTYPE_MULTIPLE ||
arch.cpusubtype == cpu_subtype)) {
offset = arch.offset;
return true;
}

View File

@@ -34,6 +34,7 @@
#ifndef COMMON_MAC_MACHO_WALKER_H__
#define COMMON_MAC_MACHO_WALKER_H__
#include <mach/machine.h>
#include <mach-o/loader.h>
#include <sys/types.h>
@@ -56,16 +57,14 @@ class MachoWalker {
void *context);
~MachoWalker();
// Begin walking the header for |cpu_type|. If |cpu_type| is 0, then the
// native cpu type is used. Otherwise, accepted values are listed in
// /usr/include/mach/machine.h (e.g., CPU_TYPE_X86 or CPU_TYPE_POWERPC).
// Returns false if opening the file failed or if the |cpu_type| is not
// present in the file.
bool WalkHeader(int cpu_type);
// Locate (if any) the header offset for |cpu_type| and return in |offset|.
// Return true if found, false otherwise.
bool FindHeader(int cpu_type, off_t &offset);
// Begin walking the header for |cpu_type| and |cpu_subtype|. If |cpu_type|
// is 0, then the native cpu type is used. Otherwise, accepted values are
// listed in /usr/include/mach/machine.h (e.g., CPU_TYPE_X86 or
// CPU_TYPE_POWERPC). If |cpu_subtype| is CPU_SUBTYPE_MULTIPLE, the match is
// only done on |cpu_type|.
// Returns false if opening the file failed or if the |cpu_type|/|cpu_subtype|
// is not present in the file.
bool WalkHeader(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype);
// Read |size| bytes from the opened file at |offset| into |buffer|
bool ReadBytes(void *buffer, size_t size, off_t offset);
@@ -74,8 +73,11 @@ class MachoWalker {
bool CurrentHeader(struct mach_header_64 *header, off_t *offset);
private:
// Validate the |cpu_type|
int ValidateCPUType(int cpu_type);
// Locate (if any) the header offset for |cpu_type| and return in |offset|.
// Return true if found, false otherwise.
bool FindHeader(cpu_type_t cpu_type,
cpu_subtype_t cpu_subtype,
off_t &offset);
// Process an individual header starting at |offset| from the start of the
// file. Return true if successful, false otherwise.

View File

@@ -27,7 +27,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "processor/scoped_ptr.h"
#include "common/scoped_ptr.h"
#include "common/mac/string_utilities.h"
namespace MacStringUtils {

File diff suppressed because it is too large Load Diff

View File

@@ -17,13 +17,20 @@
//
#import "GTMSenTestCase.h"
#import <unistd.h>
#if GTM_IPHONE_SIMULATOR
#import <objc/message.h>
#endif
#import "GTMObjC2Runtime.h"
#import "GTMUnitTestDevLog.h"
#if !GTM_IPHONE_SDK
#import "GTMGarbageCollection.h"
#endif // !GTM_IPHONE_SDK
#if GTM_IPHONE_SDK
#if GTM_IPHONE_SDK && !GTM_IPHONE_USE_SENTEST
#import <stdarg.h>
@interface NSException (GTMSenTestPrivateAdditions)
@@ -84,7 +91,7 @@
}
NSString *reason = [NSString stringWithFormat:@"'%@' should be %s. %@",
condition, isTrue ? "TRUE" : "FALSE", testDescription];
condition, isTrue ? "false" : "true", testDescription];
return [self failureInFile:filename atLine:lineNumber reason:reason];
}
@@ -213,6 +220,22 @@ NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey";
@end
@implementation SenTestCase
+ (id)testCaseWithInvocation:(NSInvocation *)anInvocation {
return [[[self alloc] initWithInvocation:anInvocation] autorelease];
}
- (id)initWithInvocation:(NSInvocation *)anInvocation {
if ((self = [super init])) {
invocation_ = [anInvocation retain];
}
return self;
}
- (void)dealloc {
[invocation_ release];
[super dealloc];
}
- (void)failWithException:(NSException*)exception {
[exception raise];
}
@@ -220,17 +243,24 @@ NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey";
- (void)setUp {
}
- (void)performTest:(SEL)sel {
currentSelector_ = sel;
- (void)performTest {
@try {
[self invokeTest];
} @catch (NSException *exception) {
[[self class] printException:exception
fromTestName:NSStringFromSelector(sel)];
fromTestName:NSStringFromSelector([self selector])];
[exception raise];
}
}
- (NSInvocation *)invocation {
return invocation_;
}
- (SEL)selector {
return [invocation_ selector];
}
+ (void)printException:(NSException *)exception fromTestName:(NSString *)name {
NSDictionary *userInfo = [exception userInfo];
NSString *filename = [userInfo objectForKey:SenTestFilenameKey];
@@ -261,7 +291,17 @@ NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey";
@try {
[self setUp];
@try {
[self performSelector:currentSelector_];
NSInvocation *invocation = [self invocation];
#if GTM_IPHONE_SIMULATOR
// We don't call [invocation invokeWithTarget:self]; because of
// Radar 8081169: NSInvalidArgumentException can't be caught
// It turns out that on iOS4 (and 3.2) exceptions thrown inside an
// [invocation invoke] on the simulator cannot be caught.
// http://openradar.appspot.com/8081169
objc_msgSend(self, [invocation selector]);
#else
[invocation invokeWithTarget:self];
#endif
} @catch (NSException *exception) {
e = [exception retain];
}
@@ -284,15 +324,84 @@ NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey";
- (NSString *)description {
// This matches the description OCUnit would return to you
return [NSString stringWithFormat:@"-[%@ %@]", [self class],
NSStringFromSelector(currentSelector_)];
return [NSString stringWithFormat:@"-[%@ %@]", [self class],
NSStringFromSelector([self selector])];
}
// Used for sorting methods below
static int MethodSort(id a, id b, void *context) {
NSInvocation *invocationA = a;
NSInvocation *invocationB = b;
const char *nameA = sel_getName([invocationA selector]);
const char *nameB = sel_getName([invocationB selector]);
return strcmp(nameA, nameB);
}
+ (NSArray *)testInvocations {
NSMutableArray *invocations = nil;
// Need to walk all the way up the parent classes collecting methods (in case
// a test is a subclass of another test).
Class senTestCaseClass = [SenTestCase class];
for (Class currentClass = self;
currentClass && (currentClass != senTestCaseClass);
currentClass = class_getSuperclass(currentClass)) {
unsigned int methodCount;
Method *methods = class_copyMethodList(currentClass, &methodCount);
if (methods) {
// This handles disposing of methods for us even if an exception should fly.
[NSData dataWithBytesNoCopy:methods
length:sizeof(Method) * methodCount];
if (!invocations) {
invocations = [NSMutableArray arrayWithCapacity:methodCount];
}
for (size_t i = 0; i < methodCount; ++i) {
Method currMethod = methods[i];
SEL sel = method_getName(currMethod);
char *returnType = NULL;
const char *name = sel_getName(sel);
// If it starts with test, takes 2 args (target and sel) and returns
// void run it.
if (strstr(name, "test") == name) {
returnType = method_copyReturnType(currMethod);
if (returnType) {
// This handles disposing of returnType for us even if an
// exception should fly. Length +1 for the terminator, not that
// the length really matters here, as we never reference inside
// the data block.
[NSData dataWithBytesNoCopy:returnType
length:strlen(returnType) + 1];
}
}
// TODO: If a test class is a subclass of another, and they reuse the
// same selector name (ie-subclass overrides it), this current loop
// and test here will cause cause it to get invoked twice. To fix this
// the selector would have to be checked against all the ones already
// added, so it only gets done once.
if (returnType // True if name starts with "test"
&& strcmp(returnType, @encode(void)) == 0
&& method_getNumberOfArguments(currMethod) == 2) {
NSMethodSignature *sig = [self instanceMethodSignatureForSelector:sel];
NSInvocation *invocation
= [NSInvocation invocationWithMethodSignature:sig];
[invocation setSelector:sel];
[invocations addObject:invocation];
}
}
}
}
// Match SenTestKit and run everything in alphbetical order.
[invocations sortUsingFunction:MethodSort context:nil];
return invocations;
}
@end
#endif // GTM_IPHONE_SDK
#endif // GTM_IPHONE_SDK && !GTM_IPHONE_USE_SENTEST
@implementation GTMTestCase : SenTestCase
- (void)invokeTest {
NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init];
Class devLogClass = NSClassFromString(@"GTMUnitTestDevLog");
if (devLogClass) {
[devLogClass performSelector:@selector(enableTracking)];
@@ -304,19 +413,34 @@ NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey";
[devLogClass performSelector:@selector(verifyNoMoreLogsExpected)];
[devLogClass performSelector:@selector(disableTracking)];
}
[localPool drain];
}
+ (BOOL)isAbstractTestCase {
NSString *name = NSStringFromClass(self);
return [name rangeOfString:@"AbstractTest"].location != NSNotFound;
}
+ (NSArray *)testInvocations {
NSArray *invocations = nil;
if (![self isAbstractTestCase]) {
invocations = [super testInvocations];
}
return invocations;
}
@end
// Leak detection
#if !GTM_IPHONE_DEVICE
#if !GTM_IPHONE_DEVICE && !GTM_SUPPRESS_RUN_LEAKS_HOOK
// Don't want to get leaks on the iPhone Device as the device doesn't
// have 'leaks'. The simulator does though.
// COV_NF_START
// We don't have leak checking on by default, so this won't be hit.
static void _GTMRunLeaks(void) {
// This is an atexit handler. It runs leaks for us to check if we are
// leaking anything in our tests.
// This is an atexit handler. It runs leaks for us to check if we are
// leaking anything in our tests.
const char* cExclusionsEnv = getenv("GTM_LEAKS_SYMBOLS_TO_IGNORE");
NSMutableString *exclusions = [NSMutableString string];
if (cExclusionsEnv) {
@@ -329,13 +453,21 @@ static void _GTMRunLeaks(void) {
[exclusions appendFormat:@"-exclude \"%@\" ", exclusion];
}
}
NSString *string
= [NSString stringWithFormat:@"/usr/bin/leaks %@%d"
@"| /usr/bin/sed -e 's/Leak: /Leaks:0: warning: Leak /'",
// Clearing out DYLD_ROOT_PATH because iPhone Simulator framework libraries
// are different from regular OS X libraries and leaks will fail to run
// because of missing symbols. Also capturing the output of leaks and then
// pipe rather than a direct pipe, because otherwise if leaks failed,
// the system() call will still be successful. Bug:
// http://code.google.com/p/google-toolbox-for-mac/issues/detail?id=56
NSString *string
= [NSString stringWithFormat:
@"LeakOut=`DYLD_ROOT_PATH='' /usr/bin/leaks %@%d` &&"
@"echo \"$LeakOut\"|/usr/bin/sed -e 's/Leak: /Leaks:0: warning: Leak /'",
exclusions, getpid()];
int ret = system([string UTF8String]);
if (ret) {
fprintf(stderr, "%s:%d: Error: Unable to run leaks. 'system' returned: %d",
fprintf(stderr,
"%s:%d: Error: Unable to run leaks. 'system' returned: %d\n",
__FILE__, __LINE__, ret);
fflush(stderr);
}
@@ -355,12 +487,14 @@ static __attribute__((constructor)) void _GTMInstallLeaks(void) {
fprintf(stderr, "Leak Checking Enabled\n");
fflush(stderr);
int ret = atexit(&_GTMRunLeaks);
_GTMDevAssert(ret == 0,
@"Unable to install _GTMRunLeaks as an atexit handler (%d)",
// To avoid unused variable warning when _GTMDevAssert is stripped.
(void)ret;
_GTMDevAssert(ret == 0,
@"Unable to install _GTMRunLeaks as an atexit handler (%d)",
errno);
// COV_NF_END
}
}
}
}
#endif // !GTM_IPHONE_DEVICE
#endif // !GTM_IPHONE_DEVICE && !GTM_SUPPRESS_RUN_LEAKS_HOOK

Some files were not shown because too many files have changed in this diff Show More