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:
parent
9f45f6b5cf
commit
593eff42ca
@ -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 \
|
||||
|
44
Makefile.in
44
Makefile.in
@ -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
|
||||
|
144
src/client/linux/minidump_writer/cpu_set.h
Normal file
144
src/client/linux/minidump_writer/cpu_set.h
Normal 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_
|
165
src/client/linux/minidump_writer/cpu_set_unittest.cc
Normal file
165
src/client/linux/minidump_writer/cpu_set_unittest.cc
Normal 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());
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,72 +1243,40 @@ 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)
|
||||
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)
|
||||
if (!my_strcmp(field, entry->info_name)) {
|
||||
size_t value_len;
|
||||
const char* value = reader->GetValueAndLen(&value_len);
|
||||
if (value_len == 0)
|
||||
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);
|
||||
|
130
src/client/linux/minidump_writer/proc_cpuinfo_reader.h
Normal file
130
src/client/linux/minidump_writer/proc_cpuinfo_reader.h
Normal 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_
|
200
src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc
Normal file
200
src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc
Normal 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));
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
127
src/common/tests/auto_testfile.h
Normal file
127
src/common/tests/auto_testfile.h
Normal 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
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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]);
|
||||
|
Loading…
Reference in New Issue
Block a user