Improve ARM CPU info reporting.

This patch improves several things for Linux/ARM:

- Better detection of the number of CPUs on the target
  device. The content of /proc/cpuinfo only matches the
  number of "online" CPUs, which varies over time with
  recent Android devices.

- Reconstruct the CPUID and ELF hwcaps values from
  /proc/cpuinfo, this is useful to better identify
  target devices in minidumps.

- Make minidump_dump display the new information
  in useful ways.

- Write a small helper class to parse /proc/cpuinfo
  and also use it for x86/64.

- Write a small helper class to parse sysfds cpu lists.

- Add a my_memchr() implementation.

- Add unit tests.

Tested on a Nexus S (1 CPU), Galaxy Nexus (2 CPUs)
and a Nexus 4 (4 CPUs).

Review URL: https://breakpad.appspot.com/540003

git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1160 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
digit@chromium.org 2013-04-24 10:06:14 +00:00
parent 9f45f6b5cf
commit 593eff42ca
15 changed files with 1249 additions and 127 deletions

View file

@ -338,12 +338,14 @@ src_client_linux_linux_dumper_unittest_helper_CC=$(PTHREAD_CC)
src_client_linux_linux_client_unittest_shlib_SOURCES = \
src/client/linux/handler/exception_handler_unittest.cc \
src/client/linux/minidump_writer/directory_reader_unittest.cc \
src/client/linux/minidump_writer/cpu_set_unittest.cc \
src/client/linux/minidump_writer/line_reader_unittest.cc \
src/client/linux/minidump_writer/linux_core_dumper.cc \
src/client/linux/minidump_writer/linux_core_dumper_unittest.cc \
src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc \
src/client/linux/minidump_writer/minidump_writer_unittest.cc \
src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc \
src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc \
src/common/linux/elf_core_dump.cc \
src/common/linux/linux_libc_support_unittest.cc \
src/common/linux/tests/crash_generator.cc \

View file

@ -448,12 +448,14 @@ src_client_linux_linux_client_unittest_LINK = $(CCLD) $(AM_CFLAGS) \
am__src_client_linux_linux_client_unittest_shlib_SOURCES_DIST = \
src/client/linux/handler/exception_handler_unittest.cc \
src/client/linux/minidump_writer/directory_reader_unittest.cc \
src/client/linux/minidump_writer/cpu_set_unittest.cc \
src/client/linux/minidump_writer/line_reader_unittest.cc \
src/client/linux/minidump_writer/linux_core_dumper.cc \
src/client/linux/minidump_writer/linux_core_dumper_unittest.cc \
src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc \
src/client/linux/minidump_writer/minidump_writer_unittest.cc \
src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc \
src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc \
src/common/linux/elf_core_dump.cc \
src/common/linux/linux_libc_support_unittest.cc \
src/common/linux/tests/crash_generator.cc \
@ -469,12 +471,14 @@ am__src_client_linux_linux_client_unittest_shlib_SOURCES_DIST = \
@ANDROID_HOST_TRUE@@LINUX_HOST_TRUE@ src/common/android/src_client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.$(OBJEXT)
@LINUX_HOST_TRUE@am_src_client_linux_linux_client_unittest_shlib_OBJECTS = src/client/linux/handler/src_client_linux_linux_client_unittest_shlib-exception_handler_unittest.$(OBJEXT) \
@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-directory_reader_unittest.$(OBJEXT) \
@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-cpu_set_unittest.$(OBJEXT) \
@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-line_reader_unittest.$(OBJEXT) \
@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-linux_core_dumper.$(OBJEXT) \
@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-linux_core_dumper_unittest.$(OBJEXT) \
@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-linux_ptrace_dumper_unittest.$(OBJEXT) \
@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-minidump_writer_unittest.$(OBJEXT) \
@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-minidump_writer_unittest_utils.$(OBJEXT) \
@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.$(OBJEXT) \
@LINUX_HOST_TRUE@ src/common/linux/src_client_linux_linux_client_unittest_shlib-elf_core_dump.$(OBJEXT) \
@LINUX_HOST_TRUE@ src/common/linux/src_client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.$(OBJEXT) \
@LINUX_HOST_TRUE@ src/common/linux/tests/src_client_linux_linux_client_unittest_shlib-crash_generator.$(OBJEXT) \
@ -1516,12 +1520,14 @@ TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
@LINUX_HOST_TRUE@src_client_linux_linux_dumper_unittest_helper_CC = $(PTHREAD_CC)
@LINUX_HOST_TRUE@src_client_linux_linux_client_unittest_shlib_SOURCES = src/client/linux/handler/exception_handler_unittest.cc \
@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/directory_reader_unittest.cc \
@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/cpu_set_unittest.cc \
@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/line_reader_unittest.cc \
@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_core_dumper.cc \
@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_core_dumper_unittest.cc \
@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc \
@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/minidump_writer_unittest.cc \
@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc \
@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc \
@LINUX_HOST_TRUE@ src/common/linux/elf_core_dump.cc \
@LINUX_HOST_TRUE@ src/common/linux/linux_libc_support_unittest.cc \
@LINUX_HOST_TRUE@ src/common/linux/tests/crash_generator.cc \
@ -2688,6 +2694,9 @@ src/client/linux/handler/src_client_linux_linux_client_unittest_shlib-exception_
src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-directory_reader_unittest.$(OBJEXT): \
src/client/linux/minidump_writer/$(am__dirstamp) \
src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp)
src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-cpu_set_unittest.$(OBJEXT): \
src/client/linux/minidump_writer/$(am__dirstamp) \
src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp)
src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-line_reader_unittest.$(OBJEXT): \
src/client/linux/minidump_writer/$(am__dirstamp) \
src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp)
@ -2706,6 +2715,9 @@ src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-mi
src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-minidump_writer_unittest_utils.$(OBJEXT): \
src/client/linux/minidump_writer/$(am__dirstamp) \
src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp)
src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.$(OBJEXT): \
src/client/linux/minidump_writer/$(am__dirstamp) \
src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp)
src/common/linux/src_client_linux_linux_client_unittest_shlib-elf_core_dump.$(OBJEXT): \
src/common/linux/$(am__dirstamp) \
src/common/linux/$(DEPDIR)/$(am__dirstamp)
@ -3359,6 +3371,7 @@ mostlyclean-compile:
-rm -f src/client/linux/minidump_writer/linux_dumper.$(OBJEXT)
-rm -f src/client/linux/minidump_writer/linux_ptrace_dumper.$(OBJEXT)
-rm -f src/client/linux/minidump_writer/minidump_writer.$(OBJEXT)
-rm -f src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-cpu_set_unittest.$(OBJEXT)
-rm -f src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-directory_reader_unittest.$(OBJEXT)
-rm -f src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-line_reader_unittest.$(OBJEXT)
-rm -f src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-linux_core_dumper.$(OBJEXT)
@ -3366,6 +3379,7 @@ mostlyclean-compile:
-rm -f src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-linux_ptrace_dumper_unittest.$(OBJEXT)
-rm -f src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-minidump_writer_unittest.$(OBJEXT)
-rm -f src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-minidump_writer_unittest_utils.$(OBJEXT)
-rm -f src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.$(OBJEXT)
-rm -f src/client/linux/minidump_writer/src_client_linux_linux_dumper_unittest_helper-linux_dumper_unittest_helper.$(OBJEXT)
-rm -f src/client/minidump_file_writer.$(OBJEXT)
-rm -f src/common/android/breakpad_getcontext.$(OBJEXT)
@ -3595,6 +3609,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/linux_dumper.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/linux_ptrace_dumper.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/minidump_writer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-cpu_set_unittest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-directory_reader_unittest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-line_reader_unittest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-linux_core_dumper.Po@am__quote@
@ -3602,6 +3617,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-linux_ptrace_dumper_unittest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-minidump_writer_unittest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-minidump_writer_unittest_utils.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_dumper_unittest_helper-linux_dumper_unittest_helper.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/convert_UTF.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/dwarf_cfi_to_module.Po@am__quote@
@ -3906,6 +3922,20 @@ src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-di
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-directory_reader_unittest.obj `if test -f 'src/client/linux/minidump_writer/directory_reader_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/directory_reader_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/directory_reader_unittest.cc'; fi`
src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-cpu_set_unittest.o: src/client/linux/minidump_writer/cpu_set_unittest.cc
@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-cpu_set_unittest.o -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-cpu_set_unittest.Tpo -c -o src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-cpu_set_unittest.o `test -f 'src/client/linux/minidump_writer/cpu_set_unittest.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/cpu_set_unittest.cc
@am__fastdepCXX_TRUE@ $(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-cpu_set_unittest.Tpo src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-cpu_set_unittest.Po
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/client/linux/minidump_writer/cpu_set_unittest.cc' object='src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-cpu_set_unittest.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-cpu_set_unittest.o `test -f 'src/client/linux/minidump_writer/cpu_set_unittest.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/cpu_set_unittest.cc
src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-cpu_set_unittest.obj: src/client/linux/minidump_writer/cpu_set_unittest.cc
@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-cpu_set_unittest.obj -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-cpu_set_unittest.Tpo -c -o src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-cpu_set_unittest.obj `if test -f 'src/client/linux/minidump_writer/cpu_set_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/cpu_set_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/cpu_set_unittest.cc'; fi`
@am__fastdepCXX_TRUE@ $(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-cpu_set_unittest.Tpo src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-cpu_set_unittest.Po
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/client/linux/minidump_writer/cpu_set_unittest.cc' object='src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-cpu_set_unittest.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-cpu_set_unittest.obj `if test -f 'src/client/linux/minidump_writer/cpu_set_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/cpu_set_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/cpu_set_unittest.cc'; fi`
src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-line_reader_unittest.o: src/client/linux/minidump_writer/line_reader_unittest.cc
@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-line_reader_unittest.o -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-line_reader_unittest.Tpo -c -o src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-line_reader_unittest.o `test -f 'src/client/linux/minidump_writer/line_reader_unittest.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/line_reader_unittest.cc
@am__fastdepCXX_TRUE@ $(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-line_reader_unittest.Tpo src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-line_reader_unittest.Po
@ -3990,6 +4020,20 @@ src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-mi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-minidump_writer_unittest_utils.obj `if test -f 'src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc'; fi`
src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.o: src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc
@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.o -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.Tpo -c -o src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.o `test -f 'src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc
@am__fastdepCXX_TRUE@ $(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.Tpo src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.Po
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc' object='src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.o `test -f 'src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc
src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.obj: src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc
@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.obj -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.Tpo -c -o src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.obj `if test -f 'src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc'; fi`
@am__fastdepCXX_TRUE@ $(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.Tpo src/client/linux/minidump_writer/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.Po
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc' object='src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/src_client_linux_linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.obj `if test -f 'src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc'; fi`
src/common/linux/src_client_linux_linux_client_unittest_shlib-elf_core_dump.o: src/common/linux/elf_core_dump.cc
@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/src_client_linux_linux_client_unittest_shlib-elf_core_dump.o -MD -MP -MF src/common/linux/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-elf_core_dump.Tpo -c -o src/common/linux/src_client_linux_linux_client_unittest_shlib-elf_core_dump.o `test -f 'src/common/linux/elf_core_dump.cc' || echo '$(srcdir)/'`src/common/linux/elf_core_dump.cc
@am__fastdepCXX_TRUE@ $(am__mv) src/common/linux/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-elf_core_dump.Tpo src/common/linux/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-elf_core_dump.Po

View file

@ -0,0 +1,144 @@
// 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 CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include "common/linux/linux_libc_support.h"
#include "third_party/lss/linux_syscall_support.h"
namespace google_breakpad {
// Helper class used to model a set of CPUs, as read from sysfs
// files like /sys/devices/system/cpu/present
// See See http://www.kernel.org/doc/Documentation/cputopology.txt
class CpuSet {
public:
// The maximum number of supported CPUs.
static const size_t kMaxCpus = 1024;
CpuSet() {
my_memset(mask_, 0, sizeof(mask_));
}
// Parse a sysfs file to extract the corresponding CPU set.
bool ParseSysFile(int fd) {
char buffer[512];
int ret = sys_read(fd, buffer, sizeof(buffer)-1);
if (ret < 0)
return false;
buffer[ret] = '\0';
// Expected format: comma-separated list of items, where each
// item can be a decimal integer, or two decimal integers separated
// by a dash.
// E.g.:
// 0
// 0,1,2,3
// 0-3
// 1,10-23
const char* p = buffer;
const char* p_end = p + ret;
while (p < p_end) {
// Skip leading space, if any
while (p < p_end && my_isspace(*p))
p++;
// Find start and size of current item.
const char* item = p;
size_t item_len = static_cast<size_t>(p_end - p);
const char* item_next =
static_cast<const char*>(my_memchr(p, ',', item_len));
if (item_next != NULL) {
p = item_next + 1;
item_len = static_cast<size_t>(item_next - item);
} else {
p = p_end;
item_next = p_end;
}
// Ignore trailing spaces.
while (item_next > item && my_isspace(item_next[-1]))
item_next--;
// skip empty items.
if (item_next == item)
continue;
// read first decimal value.
uintptr_t start = 0;
const char* next = my_read_decimal_ptr(&start, item);
uintptr_t end = start;
if (*next == '-')
my_read_decimal_ptr(&end, next+1);
while (start <= end)
SetBit(start++);
}
return true;
}
// Intersect this CPU set with another one.
void IntersectWith(const CpuSet& other) {
for (size_t nn = 0; nn < kMaskWordCount; ++nn)
mask_[nn] &= other.mask_[nn];
}
// Return the number of CPUs in this set.
int GetCount() {
int result = 0;
for (size_t nn = 0; nn < kMaskWordCount; ++nn) {
result += __builtin_popcount(mask_[nn]);
}
return result;
}
private:
void SetBit(uintptr_t index) {
size_t nn = static_cast<size_t>(index);
if (nn < kMaxCpus)
mask_[nn / kMaskWordBits] |= (1U << (nn % kMaskWordBits));
}
typedef uint32_t MaskWordType;
static const size_t kMaskWordBits = 8*sizeof(MaskWordType);
static const size_t kMaskWordCount =
(kMaxCpus + kMaskWordBits - 1) / kMaskWordBits;
MaskWordType mask_[kMaskWordCount];
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_

View file

@ -0,0 +1,165 @@
// 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.
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "breakpad_googletest_includes.h"
#include "client/linux/minidump_writer/cpu_set.h"
#include "common/linux/eintr_wrapper.h"
#include "common/tests/auto_testfile.h"
using namespace google_breakpad;
namespace {
typedef testing::Test CpuSetTest;
// Helper class to write test text file to a temporary file and return
// its file descriptor.
class ScopedTestFile : public AutoTestFile {
public:
explicit ScopedTestFile(const char* text)
: AutoTestFile("cpu_set", text) {
}
};
}
TEST(CpuSetTest, EmptyCount) {
CpuSet set;
ASSERT_EQ(0, set.GetCount());
}
TEST(CpuSetTest, OneCpu) {
ScopedTestFile file("10");
ASSERT_TRUE(file.IsOk());
CpuSet set;
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
ASSERT_EQ(1, set.GetCount());
}
TEST(CpuSetTest, OneCpuTerminated) {
ScopedTestFile file("10\n");
ASSERT_TRUE(file.IsOk());
CpuSet set;
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
ASSERT_EQ(1, set.GetCount());
}
TEST(CpuSetTest, TwoCpusWithComma) {
ScopedTestFile file("1,10");
ASSERT_TRUE(file.IsOk());
CpuSet set;
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
ASSERT_EQ(2, set.GetCount());
}
TEST(CpuSetTest, TwoCpusWithRange) {
ScopedTestFile file("1-2");
ASSERT_TRUE(file.IsOk());
CpuSet set;
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
ASSERT_EQ(2, set.GetCount());
}
TEST(CpuSetTest, TenCpusWithRange) {
ScopedTestFile file("9-18");
ASSERT_TRUE(file.IsOk());
CpuSet set;
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
ASSERT_EQ(10, set.GetCount());
}
TEST(CpuSetTest, MultiItems) {
ScopedTestFile file("0, 2-4, 128");
ASSERT_TRUE(file.IsOk());
CpuSet set;
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
ASSERT_EQ(5, set.GetCount());
}
TEST(CpuSetTest, IntersectWith) {
ScopedTestFile file1("9-19");
ASSERT_TRUE(file1.IsOk());
CpuSet set1;
ASSERT_TRUE(set1.ParseSysFile(file1.GetFd()));
ASSERT_EQ(11, set1.GetCount());
ScopedTestFile file2("16-24");
ASSERT_TRUE(file2.IsOk());
CpuSet set2;
ASSERT_TRUE(set2.ParseSysFile(file2.GetFd()));
ASSERT_EQ(9, set2.GetCount());
set1.IntersectWith(set2);
ASSERT_EQ(4, set1.GetCount());
ASSERT_EQ(9, set2.GetCount());
}
TEST(CpuSetTest, SelfIntersection) {
ScopedTestFile file1("9-19");
ASSERT_TRUE(file1.IsOk());
CpuSet set1;
ASSERT_TRUE(set1.ParseSysFile(file1.GetFd()));
ASSERT_EQ(11, set1.GetCount());
set1.IntersectWith(set1);
ASSERT_EQ(11, set1.GetCount());
}
TEST(CpuSetTest, EmptyIntersection) {
ScopedTestFile file1("0-19");
ASSERT_TRUE(file1.IsOk());
CpuSet set1;
ASSERT_TRUE(set1.ParseSysFile(file1.GetFd()));
ASSERT_EQ(20, set1.GetCount());
ScopedTestFile file2("20-39");
ASSERT_TRUE(file2.IsOk());
CpuSet set2;
ASSERT_TRUE(set2.ParseSysFile(file2.GetFd()));
ASSERT_EQ(20, set2.GetCount());
set1.IntersectWith(set2);
ASSERT_EQ(0, set1.GetCount());
ASSERT_EQ(20, set2.GetCount());
}

View file

@ -34,47 +34,41 @@
#include "client/linux/minidump_writer/line_reader.h"
#include "breakpad_googletest_includes.h"
#include "common/linux/eintr_wrapper.h"
#include "common/tests/auto_testfile.h"
using namespace google_breakpad;
#if !defined(__ANDROID__)
#define TEMPDIR "/tmp"
#else
#define TEMPDIR "/data/local/tmp"
#endif
static int TemporaryFile() {
static const char templ[] = TEMPDIR "/line-reader-unittest-XXXXXX";
char templ_copy[sizeof(templ)];
memcpy(templ_copy, templ, sizeof(templ));
const int fd = mkstemp(templ_copy);
if (fd >= 0)
unlink(templ_copy);
return fd;
}
namespace {
typedef testing::Test LineReaderTest;
class ScopedTestFile : public AutoTestFile {
public:
explicit ScopedTestFile(const char* text)
: AutoTestFile("line_reader", text) {
}
ScopedTestFile(const char* text, size_t text_len)
: AutoTestFile("line_reader", text, text_len) {
}
};
}
TEST(LineReaderTest, EmptyFile) {
const int fd = TemporaryFile();
LineReader reader(fd);
ScopedTestFile file("");
ASSERT_TRUE(file.IsOk());
LineReader reader(file.GetFd());
const char *line;
unsigned len;
ASSERT_FALSE(reader.GetNextLine(&line, &len));
close(fd);
}
TEST(LineReaderTest, OneLineTerminated) {
const int fd = TemporaryFile();
const int r = HANDLE_EINTR(write(fd, "a\n", 2));
ASSERT_EQ(2, r);
lseek(fd, 0, SEEK_SET);
LineReader reader(fd);
ScopedTestFile file("a\n");
ASSERT_TRUE(file.IsOk());
LineReader reader(file.GetFd());
const char *line;
unsigned int len;
@ -85,16 +79,12 @@ TEST(LineReaderTest, OneLineTerminated) {
reader.PopLine(len);
ASSERT_FALSE(reader.GetNextLine(&line, &len));
close(fd);
}
TEST(LineReaderTest, OneLine) {
const int fd = TemporaryFile();
const int r = HANDLE_EINTR(write(fd, "a", 1));
ASSERT_EQ(1, r);
lseek(fd, 0, SEEK_SET);
LineReader reader(fd);
ScopedTestFile file("a");
ASSERT_TRUE(file.IsOk());
LineReader reader(file.GetFd());
const char *line;
unsigned len;
@ -105,16 +95,12 @@ TEST(LineReaderTest, OneLine) {
reader.PopLine(len);
ASSERT_FALSE(reader.GetNextLine(&line, &len));
close(fd);
}
TEST(LineReaderTest, TwoLinesTerminated) {
const int fd = TemporaryFile();
const int r = HANDLE_EINTR(write(fd, "a\nb\n", 4));
ASSERT_EQ(4, r);
lseek(fd, 0, SEEK_SET);
LineReader reader(fd);
ScopedTestFile file("a\nb\n");
ASSERT_TRUE(file.IsOk());
LineReader reader(file.GetFd());
const char *line;
unsigned len;
@ -131,16 +117,12 @@ TEST(LineReaderTest, TwoLinesTerminated) {
reader.PopLine(len);
ASSERT_FALSE(reader.GetNextLine(&line, &len));
close(fd);
}
TEST(LineReaderTest, TwoLines) {
const int fd = TemporaryFile();
const int r = HANDLE_EINTR(write(fd, "a\nb", 3));
ASSERT_EQ(3, r);
lseek(fd, 0, SEEK_SET);
LineReader reader(fd);
ScopedTestFile file("a\nb");
ASSERT_TRUE(file.IsOk());
LineReader reader(file.GetFd());
const char *line;
unsigned len;
@ -157,18 +139,14 @@ TEST(LineReaderTest, TwoLines) {
reader.PopLine(len);
ASSERT_FALSE(reader.GetNextLine(&line, &len));
close(fd);
}
TEST(LineReaderTest, MaxLength) {
const int fd = TemporaryFile();
char l[LineReader::kMaxLineLen - 1];
char l[LineReader::kMaxLineLen-1];
memset(l, 'a', sizeof(l));
const int r = HANDLE_EINTR(write(fd, l, sizeof(l)));
ASSERT_EQ(static_cast<ssize_t>(sizeof(l)), r);
lseek(fd, 0, SEEK_SET);
LineReader reader(fd);
ScopedTestFile file(l, sizeof(l));
ASSERT_TRUE(file.IsOk());
LineReader reader(file.GetFd());
const char *line;
unsigned len;
@ -176,22 +154,17 @@ TEST(LineReaderTest, MaxLength) {
ASSERT_EQ(sizeof(l), len);
ASSERT_TRUE(memcmp(l, line, sizeof(l)) == 0);
ASSERT_EQ('\0', line[len]);
close(fd);
}
TEST(LineReaderTest, TooLong) {
const int fd = TemporaryFile();
// Note: this writes kMaxLineLen 'a' chars in the test file.
char l[LineReader::kMaxLineLen];
memset(l, 'a', sizeof(l));
const int r = HANDLE_EINTR(write(fd, l, sizeof(l)));
ASSERT_EQ(static_cast<ssize_t>(sizeof(l)), r);
lseek(fd, 0, SEEK_SET);
LineReader reader(fd);
ScopedTestFile file(l, sizeof(l));
ASSERT_TRUE(file.IsOk());
LineReader reader(file.GetFd());
const char *line;
unsigned len;
ASSERT_FALSE(reader.GetNextLine(&line, &len));
close(fd);
}

View file

@ -64,9 +64,11 @@
#include <algorithm>
#include "client/linux/handler/exception_handler.h"
#include "client/linux/minidump_writer/cpu_set.h"
#include "client/linux/minidump_writer/line_reader.h"
#include "client/linux/minidump_writer/linux_dumper.h"
#include "client/linux/minidump_writer/linux_ptrace_dumper.h"
#include "client/linux/minidump_writer/proc_cpuinfo_reader.h"
#include "client/minidump_file_writer.h"
#include "common/linux/linux_libc_support.h"
#include "google_breakpad/common/minidump_format.h"
@ -76,6 +78,7 @@ namespace {
using google_breakpad::AppMemoryList;
using google_breakpad::ExceptionHandler;
using google_breakpad::CpuSet;
using google_breakpad::LineReader;
using google_breakpad::LinuxDumper;
using google_breakpad::LinuxPtraceDumper;
@ -84,6 +87,7 @@ using google_breakpad::MappingInfo;
using google_breakpad::MappingList;
using google_breakpad::MinidumpFileWriter;
using google_breakpad::PageAllocator;
using google_breakpad::ProcCpuInfoReader;
using google_breakpad::ThreadInfo;
using google_breakpad::TypedMDRVA;
using google_breakpad::UntypedMDRVA;
@ -92,7 +96,7 @@ using google_breakpad::wasteful_vector;
// Minidump defines register structures which are different from the raw
// structures which we get from the kernel. These are platform specific
// functions to juggle the ucontext and user structures into minidump format.
#if defined(__i386)
#if defined(__i386__)
typedef MDRawContextX86 RawContextCPU;
// Write a uint16_t to memory
@ -597,7 +601,7 @@ class MinidumpWriter {
return;
}
}
#elif defined(__i386)
#elif defined(__i386__)
uint32_t bp = cpu->ebp;
uint32_t top = thread.stack.start_of_memory_range;
for (int i = 4; i--; ) {
@ -1163,7 +1167,7 @@ class MinidumpWriter {
return dumper_->crash_thread();
}
#if defined(__i386)
#if defined(__i386__)
uintptr_t GetStackPointer() {
return ucontext_->uc_mcontext.gregs[REG_ESP];
}
@ -1209,10 +1213,10 @@ class MinidumpWriter {
dirent->location.rva = 0;
}
#if defined(__i386__) || defined(__x86_64__)
bool WriteCPUInformation(MDRawSystemInfo* sys_info) {
char vendor_id[sizeof(sys_info->cpu.x86_cpu_info.vendor_id) + 1] = {0};
static const char vendor_id_name[] = "vendor_id";
static const size_t vendor_id_name_length = sizeof(vendor_id_name) - 1;
struct CpuInfoEntry {
const char* info_name;
@ -1220,23 +1224,17 @@ class MinidumpWriter {
bool found;
} cpu_info_table[] = {
{ "processor", -1, false },
#if defined(__i386) || defined(__x86_64)
{ "model", 0, false },
{ "stepping", 0, false },
{ "cpu family", 0, false },
#endif
};
// processor_architecture should always be set, do this first
sys_info->processor_architecture =
#if defined(__i386)
#if defined(__i386__)
MD_CPU_ARCHITECTURE_X86;
#elif defined(__x86_64)
MD_CPU_ARCHITECTURE_AMD64;
#elif defined(__arm__)
MD_CPU_ARCHITECTURE_ARM;
#else
#error "Unknown CPU arch"
MD_CPU_ARCHITECTURE_AMD64;
#endif
const int fd = sys_open("/proc/cpuinfo", O_RDONLY, 0);
@ -1245,71 +1243,39 @@ class MinidumpWriter {
{
PageAllocator allocator;
LineReader* const line_reader = new(allocator) LineReader(fd);
const char* line;
unsigned line_len;
while (line_reader->GetNextLine(&line, &line_len)) {
ProcCpuInfoReader* const reader = new(allocator) ProcCpuInfoReader(fd);
const char* field;
while (reader->GetNextField(&field)) {
for (size_t i = 0;
i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]);
i++) {
CpuInfoEntry* entry = &cpu_info_table[i];
if (entry->found && i)
if (i > 0 && entry->found) {
// except for the 'processor' field, ignore repeated values.
continue;
if (!my_strncmp(line, entry->info_name, strlen(entry->info_name))) {
const char* value = my_strchr(line, ':');
if (!value)
}
if (!my_strcmp(field, entry->info_name)) {
size_t value_len;
const char* value = reader->GetValueAndLen(&value_len);
if (value_len == 0)
continue;
// the above strncmp only matches the prefix, it might be the wrong
// line. i.e. we matched "model name" instead of "model".
// check and make sure there is only spaces between the prefix and
// the colon.
const char* space_ptr = line + my_strlen(entry->info_name);
for (; space_ptr < value; space_ptr++) {
if (!my_isspace(*space_ptr)) {
break;
}
}
if (space_ptr != value)
continue;
// skip past the colon and all the spaces that follow
do {
value++;
} while (my_isspace(*value));
uintptr_t val;
if (my_read_decimal_ptr(&val, value) == value)
continue;
entry->value = static_cast<int>(val);
entry->found = true;
}
}
// special case for vendor_id
if (!my_strncmp(line, vendor_id_name, vendor_id_name_length)) {
const char* value = my_strchr(line, ':');
if (!value)
goto popline;
// skip past the colon and all the spaces that follow
do {
value++;
} while (my_isspace(*value));
if (*value) {
size_t length = my_strlen(value);
if (length == 0)
goto popline;
if (!my_strcmp(field, vendor_id_name)) {
size_t value_len;
const char* value = reader->GetValueAndLen(&value_len);
if (value_len > 0)
my_strlcpy(vendor_id, value, sizeof(vendor_id));
// we don't want the trailing newline
if (length < sizeof(vendor_id) && vendor_id[length - 1] == '\n')
vendor_id[length - 1] = '\0';
}
}
popline:
line_reader->PopLine(line_len);
}
sys_close(fd);
}
@ -1322,15 +1288,15 @@ class MinidumpWriter {
return false;
}
}
// /proc/cpuinfo contains cpu id, change it into number by adding one.
// cpu_info_table[0] holds the last cpu id listed in /proc/cpuinfo,
// assuming this is the highest id, change it to the number of CPUs
// by adding one.
cpu_info_table[0].value++;
sys_info->number_of_processors = cpu_info_table[0].value;
#if defined(__i386) || defined(__x86_64)
sys_info->processor_level = cpu_info_table[3].value;
sys_info->processor_revision = cpu_info_table[1].value << 8 |
cpu_info_table[2].value;
#endif
if (vendor_id[0] != '\0') {
my_memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id,
@ -1338,6 +1304,199 @@ class MinidumpWriter {
}
return true;
}
#elif defined(__arm__)
bool WriteCPUInformation(MDRawSystemInfo* sys_info) {
// The CPUID value is broken up in several entries in /proc/cpuinfo.
// This table is used to rebuild it from the entries.
const struct CpuIdEntry {
const char* field;
char format;
char bit_lshift;
char bit_length;
} cpu_id_entries[] = {
{ "CPU implementer", 'x', 24, 8 },
{ "CPU variant", 'x', 20, 4 },
{ "CPU part", 'x', 4, 12 },
{ "CPU revision", 'd', 0, 4 },
};
// The ELF hwcaps are listed in the "Features" entry as textual tags.
// This table is used to rebuild them.
const struct CpuFeaturesEntry {
const char* tag;
uint32_t hwcaps;
} cpu_features_entries[] = {
{ "swp", MD_CPU_ARM_ELF_HWCAP_SWP },
{ "half", MD_CPU_ARM_ELF_HWCAP_HALF },
{ "thumb", MD_CPU_ARM_ELF_HWCAP_THUMB },
{ "26bit", MD_CPU_ARM_ELF_HWCAP_26BIT },
{ "fastmult", MD_CPU_ARM_ELF_HWCAP_FAST_MULT },
{ "fpa", MD_CPU_ARM_ELF_HWCAP_FPA },
{ "vfp", MD_CPU_ARM_ELF_HWCAP_VFP },
{ "edsp", MD_CPU_ARM_ELF_HWCAP_EDSP },
{ "java", MD_CPU_ARM_ELF_HWCAP_JAVA },
{ "iwmmxt", MD_CPU_ARM_ELF_HWCAP_IWMMXT },
{ "crunch", MD_CPU_ARM_ELF_HWCAP_CRUNCH },
{ "thumbee", MD_CPU_ARM_ELF_HWCAP_THUMBEE },
{ "neon", MD_CPU_ARM_ELF_HWCAP_NEON },
{ "vfpv3", MD_CPU_ARM_ELF_HWCAP_VFPv3 },
{ "vfpv3d16", MD_CPU_ARM_ELF_HWCAP_VFPv3D16 },
{ "tls", MD_CPU_ARM_ELF_HWCAP_TLS },
{ "vfpv4", MD_CPU_ARM_ELF_HWCAP_VFPv4 },
{ "idiva", MD_CPU_ARM_ELF_HWCAP_IDIVA },
{ "idivt", MD_CPU_ARM_ELF_HWCAP_IDIVT },
{ "idiv", MD_CPU_ARM_ELF_HWCAP_IDIVA | MD_CPU_ARM_ELF_HWCAP_IDIVT },
};
// processor_architecture should always be set, do this first
sys_info->processor_architecture = MD_CPU_ARCHITECTURE_ARM;
// /proc/cpuinfo is not readable under various sandboxed environments
// (e.g. Android services with the android:isolatedProcess attribute)
// prepare for this by setting default values now, which will be
// returned when this happens.
//
// Note: Bogus values are used to distinguish between failures (to
// read /sys and /proc files) and really badly configured kernels.
sys_info->number_of_processors = 0;
sys_info->processor_level = 1U; // There is no ARMv1
sys_info->processor_revision = 42;
sys_info->cpu.arm_cpu_info.cpuid = 0;
sys_info->cpu.arm_cpu_info.elf_hwcaps = 0;
// Counting the number of CPUs involves parsing two sysfs files,
// because the content of /proc/cpuinfo will only mirror the number
// of 'online' cores, and thus will vary with time.
// See http://www.kernel.org/doc/Documentation/cputopology.txt
{
CpuSet cpus_present;
CpuSet cpus_possible;
int fd = sys_open("/sys/devices/system/cpu/present", O_RDONLY, 0);
if (fd >= 0) {
cpus_present.ParseSysFile(fd);
sys_close(fd);
fd = sys_open("/sys/devices/system/cpu/possible", O_RDONLY, 0);
if (fd >= 0) {
cpus_possible.ParseSysFile(fd);
sys_close(fd);
cpus_present.IntersectWith(cpus_possible);
int cpu_count = cpus_present.GetCount();
if (cpu_count > 255)
cpu_count = 255;
sys_info->number_of_processors = static_cast<uint8_t>(cpu_count);
}
}
}
// Parse /proc/cpuinfo to reconstruct the CPUID value, as well
// as the ELF hwcaps field. For the latter, it would be easier to
// read /proc/self/auxv but unfortunately, this file is not always
// readable from regular Android applications on later versions
// (>= 4.1) of the Android platform.
const int fd = sys_open("/proc/cpuinfo", O_RDONLY, 0);
if (fd < 0) {
// Do not return false here to allow the minidump generation
// to happen properly.
return true;
}
{
PageAllocator allocator;
ProcCpuInfoReader* const reader =
new(allocator) ProcCpuInfoReader(fd);
const char* field;
while (reader->GetNextField(&field)) {
for (size_t i = 0;
i < sizeof(cpu_id_entries)/sizeof(cpu_id_entries[0]);
++i) {
const CpuIdEntry* entry = &cpu_id_entries[i];
if (my_strcmp(entry->field, field) != 0)
continue;
uintptr_t result = 0;
const char* value = reader->GetValue();
const char* p = value;
if (value[0] == '0' && value[1] == 'x') {
p = my_read_hex_ptr(&result, value+2);
} else if (entry->format == 'x')
p = my_read_hex_ptr(&result, value);
else
p = my_read_decimal_ptr(&result, value);
if (p == value)
continue;
result &= (1U << entry->bit_length)-1;
result <<= entry->bit_lshift;
sys_info->cpu.arm_cpu_info.cpuid |=
static_cast<uint32_t>(result);
}
// Get the architecture version from the "Processor" field.
// Note that it is also available in the "CPU architecture" field,
// however, some existing kernels are misconfigured and will report
// invalid values here (e.g. 6, while the CPU is ARMv7-A based).
// The "Processor" field doesn't have this issue.
if (!my_strcmp(field, "Processor")) {
unsigned value_len;
const char* value = reader->GetValueAndLen(&value_len);
// Expected format: <text> (v<level><endian>)
// Where <text> is some text like "ARMv7 Processor rev 2"
// and <level> is a decimal corresponding to the ARM
// architecture number. <endian> is either 'l' or 'b'
// and corresponds to the endianess, it is ignored here.
while (value_len > 0 && my_isspace(value[value_len-1]))
value_len--;
size_t nn = value_len;
while (nn > 0 && value[nn-1] != '(')
nn--;
if (nn > 0 && value[nn] == 'v') {
uintptr_t arch_level = 5;
my_read_decimal_ptr(&arch_level, value + nn + 1);
sys_info->processor_level = static_cast<uint16_t>(arch_level);
}
}
// Rebuild the ELF hwcaps from the 'Features' field.
if (!my_strcmp(field, "Features")) {
unsigned value_len;
const char* value = reader->GetValueAndLen(&value_len);
// Parse each space-separated tag.
while (value_len > 0) {
const char* tag = value;
size_t tag_len = value_len;
const char* p = my_strchr(tag, ' ');
if (p != NULL) {
tag_len = static_cast<size_t>(p - tag);
value += tag_len + 1;
value_len -= tag_len + 1;
} else {
tag_len = strlen(tag);
value_len = 0;
}
for (size_t i = 0;
i < sizeof(cpu_features_entries)/
sizeof(cpu_features_entries[0]);
++i) {
const CpuFeaturesEntry* entry = &cpu_features_entries[i];
if (tag_len == strlen(entry->tag) &&
!memcmp(tag, entry->tag, tag_len)) {
sys_info->cpu.arm_cpu_info.elf_hwcaps |= entry->hwcaps;
break;
}
}
}
}
}
sys_close(fd);
}
return true;
}
#else
# error "Unsupported CPU"
#endif
bool WriteFile(MDLocationDescriptor* result, const char* filename) {
const int fd = sys_open(filename, O_RDONLY, 0);

View file

@ -0,0 +1,130 @@
// 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 CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include "client/linux/minidump_writer/line_reader.h"
#include "common/linux/linux_libc_support.h"
#include "third_party/lss/linux_syscall_support.h"
namespace google_breakpad {
// A class for reading /proc/cpuinfo without using fopen/fgets or other
// functions which may allocate memory.
class ProcCpuInfoReader {
public:
ProcCpuInfoReader(int fd)
: line_reader_(fd), pop_count_(-1) {
}
// Return the next field name, or NULL in case of EOF.
// field: (output) Pointer to zero-terminated field name.
// Returns true on success, or false on EOF or error (line too long).
bool GetNextField(const char** field) {
for (;;) {
const char* line;
unsigned line_len;
// Try to read next line.
if (pop_count_ >= 0) {
line_reader_.PopLine(pop_count_);
pop_count_ = -1;
}
if (!line_reader_.GetNextLine(&line, &line_len))
return false;
pop_count_ = static_cast<int>(line_len);
const char* line_end = line + line_len;
// Expected format: <field-name> <space>+ ':' <space> <value>
// Note that:
// - empty lines happen.
// - <field-name> can contain spaces.
// - some fields have an empty <value>
char* sep = static_cast<char*>(my_memchr(line, ':', line_len));
if (sep == NULL)
continue;
// Record the value. Skip leading space after the column to get
// its start.
const char* val = sep+1;
while (val < line_end && my_isspace(*val))
val++;
value_ = val;
value_len_ = static_cast<size_t>(line_end - val);
// Remove trailing spaces before the column to properly 0-terminate
// the field name.
while (sep > line && my_isspace(sep[-1]))
sep--;
if (sep == line)
continue;
// zero-terminate field name.
*sep = '\0';
*field = line;
return true;
}
}
// Return the field value. This must be called after a succesful
// call to GetNextField().
const char* GetValue() {
assert(value_);
return value_;
}
// Same as GetValue(), but also returns the length in characters of
// the value.
const char* GetValueAndLen(size_t* length) {
assert(value_);
*length = value_len_;
return value_;
}
private:
LineReader line_reader_;
int pop_count_;
const char* value_;
size_t value_len_;
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_

View file

@ -0,0 +1,200 @@
// 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.
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "client/linux/minidump_writer/proc_cpuinfo_reader.h"
#include "breakpad_googletest_includes.h"
#include "common/linux/eintr_wrapper.h"
#include "common/tests/auto_testfile.h"
using namespace google_breakpad;
#if !defined(__ANDROID__)
#define TEMPDIR "/tmp"
#else
#define TEMPDIR "/data/local/tmp"
#endif
namespace {
typedef testing::Test ProcCpuInfoReaderTest;
class ScopedTestFile : public AutoTestFile {
public:
explicit ScopedTestFile(const char* text)
: AutoTestFile("proc_cpuinfo_reader", text) {
}
};
}
TEST(ProcCpuInfoReaderTest, EmptyFile) {
ScopedTestFile file("");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char *field;
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, OneLineTerminated) {
ScopedTestFile file("foo : bar\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char *field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
ASSERT_STREQ("bar", reader.GetValue());
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, OneLine) {
ScopedTestFile file("foo : bar");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char *field;
size_t value_len;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
ASSERT_STREQ("bar", reader.GetValueAndLen(&value_len));
ASSERT_EQ(3U, value_len);
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, TwoLinesTerminated) {
ScopedTestFile file("foo : bar\nzoo : tut\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char* field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
ASSERT_STREQ("bar", reader.GetValue());
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("zoo", field);
ASSERT_STREQ("tut", reader.GetValue());
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, SkipMalformedLine) {
ScopedTestFile file("this line should have a column\nfoo : bar\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char* field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
ASSERT_STREQ("bar", reader.GetValue());
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, SkipOneEmptyLine) {
ScopedTestFile file("\n\nfoo : bar\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char* field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
ASSERT_STREQ("bar", reader.GetValue());
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, SkipEmptyField) {
ScopedTestFile file(" : bar\nzoo : tut\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char* field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("zoo", field);
ASSERT_STREQ("tut", reader.GetValue());
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, SkipTwoEmptyLines) {
ScopedTestFile file("foo : bar\n\n\nfoo : bar\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char* field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
ASSERT_STREQ("bar", reader.GetValue());
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
ASSERT_STREQ("bar", reader.GetValue());
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, FieldWithSpaces) {
ScopedTestFile file("foo bar : zoo\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char* field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo bar", field);
ASSERT_STREQ("zoo", reader.GetValue());
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, EmptyValue) {
ScopedTestFile file("foo :\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char* field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
size_t value_len;
ASSERT_STREQ("", reader.GetValueAndLen(&value_len));
ASSERT_EQ(0U, value_len);
ASSERT_FALSE(reader.GetNextField(&field));
}

View file

@ -138,6 +138,16 @@ const char* my_strrchr(const char* haystack, char needle) {
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

View file

@ -77,6 +77,8 @@ extern const char* my_read_decimal_ptr(uintptr_t* result, const char* s);
extern void my_memset(void* ip, char c, size_t len);
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

View file

@ -154,6 +154,18 @@ TEST(LinuxLibcSupportTest, strrchr) {
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) {
uintptr_t result;
const char* last;

View file

@ -0,0 +1,127 @@
// 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. Only supported on Posix systems.
#ifndef GOOGLE_BREAKPAD_COMMON_TESTS_AUTO_TESTFILE
#define GOOGLE_BREAKPAD_COMMON_TESTS_AUTO_TESTFILE
#include <unistd.h>
#include <sys/types.h>
#include <string>
#include "breakpad_googletest_includes.h"
#include "common/tests/auto_tempdir.h"
namespace google_breakpad {
#ifdef _WIN32
#error "This header cannot be used on Windows"
#else
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) {
int r = HANDLE_EINTR(write(fd_, text, text_len));
if (r != static_cast<int>(text_len)) {
close(fd_);
fd_ = -1;
return;
}
lseek(fd_, 0, SEEK_SET);
}
int fd_;
};
#endif // !_WIN32
} // namespace google_breakpad
#endif // GOOGLE_BREAKPAD_COMMON_TESTS_AUTO_TESTFILE

View file

@ -551,19 +551,48 @@ typedef union {
uint32_t feature_information; /* cpuid 1: edx */
uint32_t amd_extended_cpu_features; /* cpuid 0x80000001, ebx */
} x86_cpu_info;
struct {
uint32_t cpuid;
uint32_t elf_hwcaps; /* linux specific, 0 otherwise */
} arm_cpu_info;
struct {
uint64_t processor_features[2];
} other_cpu_info;
} MDCPUInformation; /* CPU_INFORMATION */
/* For (MDCPUInformation).arm_cpu_info.elf_hwcaps.
* This matches the Linux kernel definitions from <asm/hwcaps.h> */
typedef enum {
MD_CPU_ARM_ELF_HWCAP_SWP = (1 << 0),
MD_CPU_ARM_ELF_HWCAP_HALF = (1 << 1),
MD_CPU_ARM_ELF_HWCAP_THUMB = (1 << 2),
MD_CPU_ARM_ELF_HWCAP_26BIT = (1 << 3),
MD_CPU_ARM_ELF_HWCAP_FAST_MULT = (1 << 4),
MD_CPU_ARM_ELF_HWCAP_FPA = (1 << 5),
MD_CPU_ARM_ELF_HWCAP_VFP = (1 << 6),
MD_CPU_ARM_ELF_HWCAP_EDSP = (1 << 7),
MD_CPU_ARM_ELF_HWCAP_JAVA = (1 << 8),
MD_CPU_ARM_ELF_HWCAP_IWMMXT = (1 << 9),
MD_CPU_ARM_ELF_HWCAP_CRUNCH = (1 << 10),
MD_CPU_ARM_ELF_HWCAP_THUMBEE = (1 << 11),
MD_CPU_ARM_ELF_HWCAP_NEON = (1 << 12),
MD_CPU_ARM_ELF_HWCAP_VFPv3 = (1 << 13),
MD_CPU_ARM_ELF_HWCAP_VFPv3D16 = (1 << 14),
MD_CPU_ARM_ELF_HWCAP_TLS = (1 << 15),
MD_CPU_ARM_ELF_HWCAP_VFPv4 = (1 << 16),
MD_CPU_ARM_ELF_HWCAP_IDIVA = (1 << 17),
MD_CPU_ARM_ELF_HWCAP_IDIVT = (1 << 18),
} MDCPUInformationARMElfHwCaps;
typedef struct {
/* The next 3 fields and numberOfProcessors are from the SYSTEM_INFO
* structure as returned by GetSystemInfo */
uint16_t processor_architecture;
uint16_t processor_level; /* x86: 5 = 586, 6 = 686, ... */
/* ARM: 6 = ARMv6, 7 = ARMv7 ... */
uint16_t processor_revision; /* x86: 0xMMSS, where MM=model,
* SS=stepping */
/* ARM: 0 */
uint8_t number_of_processors;
uint8_t product_type; /* Windows: VER_NT_* from WinNT.h */
@ -626,7 +655,6 @@ typedef enum {
MD_OS_ANDROID = 0x8203 /* Android */
} MDOSPlatform;
typedef struct {
uint32_t size_of_info; /* Length of entire MDRawMiscInfo structure. */
uint32_t flags1;

View file

@ -305,6 +305,128 @@ static const MDRawSystemInfo* GetSystemInfo(Minidump *dump,
return minidump_system_info->system_info();
}
// Extract CPU info string from ARM-specific MDRawSystemInfo structure.
// raw_info: pointer to source MDRawSystemInfo.
// cpu_info: address of target string, cpu info text will be appended to it.
static void GetARMCpuInfo(const MDRawSystemInfo* raw_info,
std::string* cpu_info) {
assert(raw_info != NULL && cpu_info != NULL);
// Write ARM architecture version.
char cpu_string[32];
snprintf(cpu_string, sizeof(cpu_string), "ARMv%d",
raw_info->processor_level);
cpu_info->append(cpu_string);
// There is no good list of implementer id values, but the following
// pages provide some help:
// http://comments.gmane.org/gmane.linux.linaro.devel/6903
// http://forum.xda-developers.com/archive/index.php/t-480226.html
const struct {
uint32_t id;
const char* name;
} vendors[] = {
{ 0x41, "ARM" },
{ 0x51, "Qualcomm" },
{ 0x56, "Marvell" },
{ 0x69, "Intel/Marvell" },
};
const struct {
uint32_t id;
const char* name;
} parts[] = {
{ 0x4100c050, "Cortex-A5" },
{ 0x4100c080, "Cortex-A8" },
{ 0x4100c090, "Cortex-A9" },
{ 0x4100c0f0, "Cortex-A15" },
{ 0x4100c140, "Cortex-R4" },
{ 0x4100c150, "Cortex-R5" },
{ 0x4100b360, "ARM1136" },
{ 0x4100b560, "ARM1156" },
{ 0x4100b760, "ARM1176" },
{ 0x4100b020, "ARM11-MPCore" },
{ 0x41009260, "ARM926" },
{ 0x41009460, "ARM946" },
{ 0x41009660, "ARM966" },
{ 0x510006f0, "Krait" },
{ 0x510000f0, "Scorpion" },
};
const struct {
uint32_t hwcap;
const char* name;
} features[] = {
{ MD_CPU_ARM_ELF_HWCAP_SWP, "swp" },
{ MD_CPU_ARM_ELF_HWCAP_HALF, "half" },
{ MD_CPU_ARM_ELF_HWCAP_THUMB, "thumb" },
{ MD_CPU_ARM_ELF_HWCAP_26BIT, "26bit" },
{ MD_CPU_ARM_ELF_HWCAP_FAST_MULT, "fastmult" },
{ MD_CPU_ARM_ELF_HWCAP_FPA, "fpa" },
{ MD_CPU_ARM_ELF_HWCAP_VFP, "vfpv2" },
{ MD_CPU_ARM_ELF_HWCAP_EDSP, "edsp" },
{ MD_CPU_ARM_ELF_HWCAP_JAVA, "java" },
{ MD_CPU_ARM_ELF_HWCAP_IWMMXT, "iwmmxt" },
{ MD_CPU_ARM_ELF_HWCAP_CRUNCH, "crunch" },
{ MD_CPU_ARM_ELF_HWCAP_THUMBEE, "thumbee" },
{ MD_CPU_ARM_ELF_HWCAP_NEON, "neon" },
{ MD_CPU_ARM_ELF_HWCAP_VFPv3, "vfpv3" },
{ MD_CPU_ARM_ELF_HWCAP_VFPv3D16, "vfpv3d16" },
{ MD_CPU_ARM_ELF_HWCAP_TLS, "tls" },
{ MD_CPU_ARM_ELF_HWCAP_VFPv4, "vfpv4" },
{ MD_CPU_ARM_ELF_HWCAP_IDIVA, "idiva" },
{ MD_CPU_ARM_ELF_HWCAP_IDIVT, "idivt" },
};
uint32_t cpuid = raw_info->cpu.arm_cpu_info.cpuid;
if (cpuid != 0) {
// Extract vendor name from CPUID
const char* vendor = NULL;
uint32_t vendor_id = (cpuid >> 24) & 0xff;
for (size_t i = 0; i < sizeof(vendors)/sizeof(vendors[0]); ++i) {
if (vendors[i].id == vendor_id) {
vendor = vendors[i].name;
break;
}
}
cpu_info->append(" ");
if (vendor) {
cpu_info->append(vendor);
} else {
snprintf(cpu_string, sizeof(cpu_string), "vendor(0x%x)", vendor_id);
cpu_info->append(cpu_string);
}
// Extract part name from CPUID
uint32_t part_id = (cpuid & 0xff00fff0);
const char* part = NULL;
for (size_t i = 0; i < sizeof(parts)/sizeof(parts[0]); ++i) {
if (parts[i].id == part_id) {
part = parts[i].name;
break;
}
}
cpu_info->append(" ");
if (part != NULL) {
cpu_info->append(part);
} else {
snprintf(cpu_string, sizeof(cpu_string), "part(0x%x)", part_id);
cpu_info->append(cpu_string);
}
}
uint32_t elf_hwcaps = raw_info->cpu.arm_cpu_info.elf_hwcaps;
if (elf_hwcaps != 0) {
cpu_info->append(" features: ");
const char* comma = "";
for (size_t i = 0; i < sizeof(features)/sizeof(features[0]); ++i) {
if (elf_hwcaps & features[i].hwcap) {
cpu_info->append(comma);
cpu_info->append(features[i].name);
comma = ",";
}
}
}
}
// static
bool MinidumpProcessor::GetCPUInfo(Minidump *dump, SystemInfo *info) {
assert(dump);
@ -359,6 +481,7 @@ bool MinidumpProcessor::GetCPUInfo(Minidump *dump, SystemInfo *info) {
case MD_CPU_ARCHITECTURE_ARM: {
info->cpu = "arm";
GetARMCpuInfo(raw_system_info, &info->cpu_info);
break;
}

View file

@ -77,6 +77,9 @@ SystemInfo::SystemInfo(const Dump &dump,
D32(system_info.cpu.x86_cpu_info.version_information);
D32(system_info.cpu.x86_cpu_info.feature_information);
D32(system_info.cpu.x86_cpu_info.amd_extended_cpu_features);
} else if (system_info.processor_architecture == MD_CPU_ARCHITECTURE_ARM) {
D32(system_info.cpu.arm_cpu_info.cpuid);
D32(system_info.cpu.arm_cpu_info.elf_hwcaps);
} else {
D64(system_info.cpu.other_cpu_info.processor_features[0]);
D64(system_info.cpu.other_cpu_info.processor_features[1]);