From c1f6e323d3ac78d46d7ac6e9cd594b0a60e6eacd Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Wed, 16 Dec 2009 15:47:12 +0000 Subject: [PATCH 01/14] Bug 534919: do not inherit font from ZWNJ to adjacent character. r=roc --- gfx/thebes/public/gfxFontUtils.h | 6 ++---- gfx/thebes/src/gfxFont.cpp | 6 +++--- layout/reftests/bugs/534919-1-ref.html | 17 +++++++++++++++++ layout/reftests/bugs/534919-1.html | 17 +++++++++++++++++ layout/reftests/bugs/reftest.list | 1 + 5 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 layout/reftests/bugs/534919-1-ref.html create mode 100644 layout/reftests/bugs/534919-1.html diff --git a/gfx/thebes/public/gfxFontUtils.h b/gfx/thebes/public/gfxFontUtils.h index a9b3f58eb8e..ec21b32a253 100644 --- a/gfx/thebes/public/gfxFontUtils.h +++ b/gfx/thebes/public/gfxFontUtils.h @@ -542,10 +542,8 @@ public: PRUint32 aPlatformCode, PRUint32 aScriptCode, PRUint32 aLangCode, nsAString& dest); - static inline bool IsJoiner(PRUint32 ch) { - return (ch == 0x200C || - ch == 0x200D || - ch == 0x2060); + static inline bool IsJoinCauser(PRUint32 ch) { + return (ch == 0x200D); } static inline bool IsInvalid(PRUint32 ch) { diff --git a/gfx/thebes/src/gfxFont.cpp b/gfx/thebes/src/gfxFont.cpp index 72653ea7de1..9512c1f7376 100644 --- a/gfx/thebes/src/gfxFont.cpp +++ b/gfx/thebes/src/gfxFont.cpp @@ -1564,9 +1564,9 @@ gfxFontGroup::FindFontForChar(PRUint32 aCh, PRUint32 aPrevCh, PRUint32 aNextCh, { nsRefPtr selectedFont; - // if this character or the next one is a joiner use the - // same font as the previous range if we can - if (gfxFontUtils::IsJoiner(aCh) || gfxFontUtils::IsJoiner(aPrevCh) || gfxFontUtils::IsJoiner(aNextCh)) { + // if this character or the previous one is a join-causer, + // use the same font as the previous range if we can + if (gfxFontUtils::IsJoinCauser(aCh) || gfxFontUtils::IsJoinCauser(aPrevCh)) { if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) { selectedFont = aPrevMatchedFont; return selectedFont.forget(); diff --git a/layout/reftests/bugs/534919-1-ref.html b/layout/reftests/bugs/534919-1-ref.html new file mode 100644 index 00000000000..2604fbf38f1 --- /dev/null +++ b/layout/reftests/bugs/534919-1-ref.html @@ -0,0 +1,17 @@ + + + + +

گ ز

+

گ ز

+

گ ز

+

گ ز

+

گ ز

+ + diff --git a/layout/reftests/bugs/534919-1.html b/layout/reftests/bugs/534919-1.html new file mode 100644 index 00000000000..da308b9c35d --- /dev/null +++ b/layout/reftests/bugs/534919-1.html @@ -0,0 +1,17 @@ + + + + +

گ ز

+

‌گ ز

+

گ‌ ز

+

گ ‌ز

+

گ ز‌

+ + diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index e1f0748dc80..0c92bd1ec65 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1353,3 +1353,4 @@ fails-if(MOZ_WIDGET_TOOLKIT!="cocoa") == 488692-1.html 488692-1-ref.html # needs == 530686-1.html 530686-1-ref.html == 531098-1.html 531098-1-ref.html == 531371-1.html 531371-1-ref.html +== 534919-1.html 534919-1-ref.html From a80df572288ddb75c48354f2cb99bb9235b0890c Mon Sep 17 00:00:00 2001 From: Steven Michaud Date: Wed, 16 Dec 2009 10:45:53 -0600 Subject: [PATCH 02/14] Bug 396680: [10.5] Crash on attempt to print to unreachable printer. r=josh --- widget/src/cocoa/nsAppShell.mm | 50 ++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/widget/src/cocoa/nsAppShell.mm b/widget/src/cocoa/nsAppShell.mm index 8b8ef686d55..003b58dcde6 100644 --- a/widget/src/cocoa/nsAppShell.mm +++ b/widget/src/cocoa/nsAppShell.mm @@ -41,6 +41,7 @@ */ #import +#include #include "nsAppShell.h" #include "nsCOMPtr.h" @@ -431,6 +432,17 @@ nsAppShell::Init() @selector(nsAppShell_NSApplication_beginModalSessionForWindow:)); nsToolkit::SwizzleMethods([NSApplication class], @selector(endModalSession:), @selector(nsAppShell_NSApplication_endModalSession:)); + if (nsToolkit::OnLeopardOrLater() && !nsToolkit::OnSnowLeopardOrLater()) { + dlopen("/System/Library/Frameworks/Carbon.framework/Frameworks/Print.framework/Versions/Current/Plugins/PrintCocoaUI.bundle/Contents/MacOS/PrintCocoaUI", + RTLD_LAZY); + Class PDEPluginCallbackClass = ::NSClassFromString(@"PDEPluginCallback"); + nsresult rv1 = nsToolkit::SwizzleMethods(PDEPluginCallbackClass, @selector(initWithPrintWindowController:), + @selector(nsAppShell_PDEPluginCallback_initWithPrintWindowController:)); + if (NS_SUCCEEDED(rv1)) { + nsToolkit::SwizzleMethods(PDEPluginCallbackClass, @selector(dealloc), + @selector(nsAppShell_PDEPluginCallback_dealloc)); + } + } gAppShellMethodsSwizzled = PR_TRUE; } @@ -1104,3 +1116,41 @@ nsAppShell::AfterProcessNextEvent(nsIThreadInternal *aThread, } @end + +@interface PDEPluginCallback : NSObject +{ +@public + id _printWindowController; +} +- (PMPrintSettings *)printSettings; +@end + +@interface PDEPluginCallback (MethodSwizzling) +- (PDEPluginCallback *)nsAppShell_PDEPluginCallback_initWithPrintWindowController:(id)controller; +- (void)nsAppShell_PDEPluginCallback_dealloc; +@end + +@implementation PDEPluginCallback (MethodSwizzling) + +// On Leopard, the PDEPluginCallback class in Apple's PrintCocoaUI module +// fails to retain and release its PMPrintWindowController object. This +// causes the PMPrintWindowController to sometimes be deleted prematurely, +// leading to crashes on attempts to access it. One example is bug 396680, +// caused by attempting to call a deleted PMPrintWindowController object's +// printSettings method. We work around the problem by hooking the +// appropriate methods and retaining and releasing the object ourselves. +// PrintCocoaUI.bundle is a "plugin" of the Carbon framework's Print +// framework. + +- (PDEPluginCallback *)nsAppShell_PDEPluginCallback_initWithPrintWindowController:(id)controller +{ + return [self nsAppShell_PDEPluginCallback_initWithPrintWindowController:[controller retain]]; +} + +- (void)nsAppShell_PDEPluginCallback_dealloc +{ + [self->_printWindowController release]; + [self nsAppShell_PDEPluginCallback_dealloc]; +} + +@end From f2d1f699b139f6248088fad01983518dcf7c9c26 Mon Sep 17 00:00:00 2001 From: Benjamin Smedberg Date: Wed, 16 Dec 2009 13:27:55 -0500 Subject: [PATCH 03/14] Added tag last-mozilla-central for changeset dba2abb7db57 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 6abd18a0c9b..d7911d62337 100644 --- a/.hgtags +++ b/.hgtags @@ -45,3 +45,4 @@ fe9cc55b8db7f56f7e68a246acba363743854979 UPDATE_PACKAGING_R8 a732c6d3c078f80635255c78bfaadffa5828a8a5 last-mozilla-central a732c6d3c078f80635255c78bfaadffa5828a8a5 last-mozilla-central 925595f3c08634cc42e33158ea6858bb55623ef7 last-mozilla-central +dba2abb7db57078c5a4810884834d3056a5d56c2 last-mozilla-central From dd730e921c5cc92e5301db59eb6ac0384feb967f Mon Sep 17 00:00:00 2001 From: Benjamin Smedberg Date: Wed, 16 Dec 2009 13:37:14 -0500 Subject: [PATCH 04/14] Reland the changes from mozilla-central that I accidentally reverted in revision 5c9ee961025c --- accessible/src/atk/nsAccessibleWrap.cpp | 2 +- accessible/src/base/nsRootAccessible.cpp | 7 +- toolkit/crashreporter/Makefile.in | 1 - toolkit/crashreporter/client/Makefile.in | 5 + .../src/client/linux/data/linux-gate-amd.sym | 3 - .../client/linux/data/linux-gate-intel.sym | 3 - .../src/client/linux/handler/Makefile.in | 2 + .../client/linux/handler/exception_handler.cc | 460 ++- .../client/linux/handler/exception_handler.h | 142 +- .../linux/handler/exception_handler_test.cc | 124 + .../handler/exception_handler_unittest.cc | 256 -- .../src/client/linux/handler/linux_thread.cc | 411 +++ .../src/client/linux/handler/linux_thread.h | 204 ++ .../client/linux/handler/linux_thread_test.cc | 224 ++ .../linux/handler/minidump_generator.cc | 816 +++++ .../minidump_generator.h} | 52 +- .../minidump_test.cc} | 87 +- .../client/linux/minidump_writer/Makefile.in | 60 - .../linux/minidump_writer/directory_reader.h | 105 - .../directory_reader_unittest.cc | 77 - .../linux/minidump_writer/line_reader.h | 130 - .../minidump_writer/line_reader_unittest.cc | 184 -- .../linux/minidump_writer/linux_dumper.cc | 421 --- .../linux/minidump_writer/linux_dumper.h | 146 - .../minidump_writer/linux_dumper_unittest.cc | 118 - .../linux/minidump_writer/minidump_writer.cc | 872 ----- .../sender/google_crash_report_sender.cc | 102 - .../mac/Breakpad.xcodeproj/project.pbxproj | 155 - .../src/client/mac/Framework/Breakpad.h | 11 +- .../src/client/mac/Framework/Breakpad.mm | 114 +- .../client/mac/crash_generation/Inspector.mm | 21 +- .../client/mac/handler/exception_handler.cc | 8 +- .../client/mac/handler/minidump_generator.cc | 15 - .../client/mac/handler/minidump_generator.h | 2 +- .../mac/sender/Breakpad.nib/classes.nib | 14 +- .../client/mac/sender/Breakpad.nib/info.nib | 6 +- .../mac/sender/Breakpad.nib/keyedobjects.nib | Bin 14674 -> 13747 bytes .../sender/English.lproj/Localizable.strings | Bin 2270 -> 1488 bytes .../client/mac/sender/crash_report_sender.h | 49 +- .../client/mac/sender/crash_report_sender.m | 220 +- .../src/client/mac/sender/goArrow.png | Bin 3591 -> 0 bytes .../mac/tests/BreakpadFramework_Test.mm | 5 +- .../src/client/minidump_file_writer.cc | 21 +- .../src/client/windows/breakpad_client.sln | 10 - .../crash_generation_server.cc | 14 +- .../windows/handler/exception_handler.cc | 10 +- .../exception_handler_test.cc | 164 - .../exception_handler_test.vcproj | 266 -- .../crash_generation_app.cc | 3 +- .../google-breakpad/src/common/Makefile.in | 6 + .../src/common/linux/Makefile.in | 6 +- .../src/common/linux/dump_symbols.cc | 809 +++-- .../src/common/linux/file_id.cc | 112 +- .../src/common/linux/file_id.h | 18 +- .../common/linux/google_crashdump_uploader.cc | 196 -- .../common/linux/google_crashdump_uploader.h | 98 - .../linux/google_crashdump_uploader_test.cc | 166 - .../src/common/linux/libcurl_wrapper.cc | 209 -- .../src/common/linux/libcurl_wrapper.h | 82 - .../src/common/linux/linux_libc_support.h | 178 -- .../linux/linux_libc_support_unittest.cc | 153 - .../src/common/linux/linux_syscall_support.h | 2800 ----------------- .../google-breakpad/src/common/linux/memory.h | 181 -- .../src/common/linux/memory_unittest.cc | 84 - .../src/common/linux/module.cc | 167 - .../google-breakpad/src/common/linux/module.h | 193 -- .../src/common/linux/stabs_reader.cc | 195 -- .../src/common/linux/stabs_reader.h | 188 -- .../src/common/mac/SimpleStringDictionary.mm | 2 - .../src/common/mac/dump_syms.h | 2 +- .../src/common/mac/dump_syms.mm | 24 +- .../src/common/mac/dwarf/bytereader-inl.h | 40 +- .../src/common/mac/dwarf/bytereader.cc | 3 +- .../src/common/mac/dwarf/dwarf2reader.cc | 14 +- .../src/common/mac/dwarf/dwarf2reader.h | 5 +- .../src/common/mac/dwarf/functioninfo.cc | 13 +- .../common/windows/pdb_source_line_writer.cc | 84 +- .../common/windows/pdb_source_line_writer.h | 45 +- .../common/minidump_exception_win32.h | 5 +- .../processor/basic_source_line_resolver.h | 24 + .../src/google_breakpad/processor/minidump.h | 41 - .../processor/minidump_processor.h | 5 - .../google_breakpad/processor/process_state.h | 13 +- .../processor/stack_frame_cpu.h | 22 +- .../google_breakpad/processor/stackwalker.h | 11 - .../processor/basic_source_line_resolver.cc | 13 + .../google-breakpad/src/processor/minidump.cc | 134 +- .../src/processor/minidump_dump.cc | 8 - .../src/processor/minidump_processor.cc | 67 +- .../src/processor/minidump_stackwalk.cc | 37 +- .../processor/pathname_stripper_unittest.cc | 2 - .../src/processor/postfix_evaluator-inl.h | 1 - .../src/processor/process_state.cc | 1 - .../src/processor/stackwalker.cc | 34 - .../src/processor/stackwalker_x86.cc | 82 +- .../src/processor/stackwalker_x86.h | 13 - .../testdata/minidump2.stackwalk.out | 4 - .../third_party/linux/include/gflags/gflags.h | 533 ---- .../linux/include/gflags/gflags_completions.h | 121 - .../linux/include/glog/log_severity.h | 84 - .../third_party/linux/include/glog/logging.h | 1499 --------- .../linux/include/glog/raw_logging.h | 185 -- .../linux/include/glog/stl_logging.h | 154 - .../linux/include/glog/vlog_is_on.h | 128 - .../third_party/linux/lib/gflags/libgflags.a | Bin 390804 -> 0 bytes .../src/third_party/linux/lib/glog/libglog.a | Bin 226928 -> 0 bytes .../tools/linux/md2core/minidump-2-core.cc | 603 ---- .../tools/mac/crash_report/crash_report.mm | 64 +- toolkit/toolkit-makefiles.sh | 1 - toolkit/xre/Makefile.in | 1 - 110 files changed, 3083 insertions(+), 13002 deletions(-) delete mode 100644 toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-amd.sym delete mode 100644 toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-intel.sym create mode 100644 toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_test.cc delete mode 100644 toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_unittest.cc create mode 100644 toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.cc create mode 100644 toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.h create mode 100644 toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread_test.cc create mode 100644 toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.cc rename toolkit/crashreporter/google-breakpad/src/client/linux/{minidump_writer/minidump_writer.h => handler/minidump_generator.h} (61%) rename toolkit/crashreporter/google-breakpad/src/client/linux/{minidump_writer/minidump_writer_unittest.cc => handler/minidump_test.cc} (57%) delete mode 100644 toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/Makefile.in delete mode 100644 toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader.h delete mode 100644 toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader_unittest.cc delete mode 100644 toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader.h delete mode 100644 toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader_unittest.cc delete mode 100644 toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.cc delete mode 100644 toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.h delete mode 100644 toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest.cc delete mode 100644 toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc delete mode 100644 toolkit/crashreporter/google-breakpad/src/client/linux/sender/google_crash_report_sender.cc delete mode 100644 toolkit/crashreporter/google-breakpad/src/client/mac/sender/goArrow.png delete mode 100644 toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.cc delete mode 100644 toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.vcproj delete mode 100644 toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.cc delete mode 100644 toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.h delete mode 100644 toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader_test.cc delete mode 100644 toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.cc delete mode 100644 toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.h delete mode 100644 toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.h delete mode 100644 toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support_unittest.cc delete mode 100644 toolkit/crashreporter/google-breakpad/src/common/linux/linux_syscall_support.h delete mode 100644 toolkit/crashreporter/google-breakpad/src/common/linux/memory.h delete mode 100644 toolkit/crashreporter/google-breakpad/src/common/linux/memory_unittest.cc delete mode 100644 toolkit/crashreporter/google-breakpad/src/common/linux/module.cc delete mode 100644 toolkit/crashreporter/google-breakpad/src/common/linux/module.h delete mode 100644 toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.cc delete mode 100644 toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.h delete mode 100644 toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags.h delete mode 100644 toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags_completions.h delete mode 100644 toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/log_severity.h delete mode 100644 toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/logging.h delete mode 100644 toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/raw_logging.h delete mode 100644 toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/stl_logging.h delete mode 100644 toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/vlog_is_on.h delete mode 100644 toolkit/crashreporter/google-breakpad/src/third_party/linux/lib/gflags/libgflags.a delete mode 100644 toolkit/crashreporter/google-breakpad/src/third_party/linux/lib/glog/libglog.a delete mode 100644 toolkit/crashreporter/google-breakpad/src/tools/linux/md2core/minidump-2-core.cc diff --git a/accessible/src/atk/nsAccessibleWrap.cpp b/accessible/src/atk/nsAccessibleWrap.cpp index ec9a80d74b2..3a087b0eadb 100644 --- a/accessible/src/atk/nsAccessibleWrap.cpp +++ b/accessible/src/atk/nsAccessibleWrap.cpp @@ -925,10 +925,10 @@ refChildCB(AtkObject *aAtkObj, gint aChildIndex) NS_ASSERTION(childAtkObj, "Fail to get AtkObj"); if (!childAtkObj) return nsnull; - g_object_ref(childAtkObj); //this will addref parent atk_object_set_parent(childAtkObj, aAtkObj); + g_object_ref(childAtkObj); return childAtkObj; } diff --git a/accessible/src/base/nsRootAccessible.cpp b/accessible/src/base/nsRootAccessible.cpp index 408d3a69c35..04d333ad573 100644 --- a/accessible/src/base/nsRootAccessible.cpp +++ b/accessible/src/base/nsRootAccessible.cpp @@ -947,12 +947,9 @@ nsRootAccessible::Init() nsRefPtr root = GetApplicationAccessible(); NS_ENSURE_STATE(root); - nsresult rv = nsDocAccessibleWrap::Init(); + root->AddRootAccessible(this); - if (NS_SUCCEEDED(rv)) - root->AddRootAccessible(this); - - return rv; + return nsDocAccessibleWrap::Init(); } nsresult diff --git a/toolkit/crashreporter/Makefile.in b/toolkit/crashreporter/Makefile.in index 9f36ffb208b..7f5f789408e 100644 --- a/toolkit/crashreporter/Makefile.in +++ b/toolkit/crashreporter/Makefile.in @@ -78,7 +78,6 @@ DIRS += \ google-breakpad/src/common/linux \ google-breakpad/src/client \ google-breakpad/src/client/linux/handler \ - google-breakpad/src/client/linux/minidump_writer \ google-breakpad/src/tools/linux/dump_syms \ $(NULL) endif diff --git a/toolkit/crashreporter/client/Makefile.in b/toolkit/crashreporter/client/Makefile.in index f16b8baa235..4eb5bff190b 100644 --- a/toolkit/crashreporter/client/Makefile.in +++ b/toolkit/crashreporter/client/Makefile.in @@ -84,6 +84,7 @@ LIBS += \ LOCAL_INCLUDES += -I$(srcdir) OS_CXXFLAGS += $(MOZ_GTK2_CFLAGS) $(MOZ_GTHREAD_CFLAGS) OS_LIBS += $(MOZ_GTK2_LIBS) $(MOZ_GTHREAD_LIBS) +CPPSRCS += http_upload.cc FORCE_USE_PIC=1 endif @@ -95,6 +96,7 @@ LIBS += \ LOCAL_INCLUDES += -I$(srcdir) OS_CXXFLAGS += $(MOZ_GTK2_CFLAGS) $(MOZ_GTHREAD_CFLAGS) OS_LIBS += $(MOZ_GTK2_LIBS) $(MOZ_GTHREAD_LIBS) +CPPSRCS += http_upload.cc FORCE_USE_PIC=1 endif @@ -112,6 +114,9 @@ libs:: endif ifeq (,$(filter-out Linux SunOS,$(OS_ARCH))) +export:: $(srcdir)/../google-breakpad/src/common/linux/http_upload.cc + $(INSTALL) $^ . + libs:: $(topsrcdir)/toolkit/themes/winstripe/global/throbber/Throbber-small.gif $(INSTALL) $^ $(DIST)/bin endif diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-amd.sym b/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-amd.sym deleted file mode 100644 index e042a5ec42d..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-amd.sym +++ /dev/null @@ -1,3 +0,0 @@ -MODULE Linux x86 B8CFDE93002D54DA1900A40AA1BD67690 linux-gate.so -PUBLIC 400 0 __kernel_vsyscall -STACK WIN 4 400 100 1 1 0 0 0 0 0 1 diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-intel.sym b/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-intel.sym deleted file mode 100644 index c209c237564..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-intel.sym +++ /dev/null @@ -1,3 +0,0 @@ -MODULE Linux x86 4FBDA58B5A1DF5A379E3CF19A235EA090 linux-gate.so -PUBLIC 400 0 __kernel_vsyscall -STACK WIN 4 400 200 3 3 0 0 0 0 0 1 \ No newline at end of file diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/Makefile.in b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/Makefile.in index 214c1298f41..8e0c04ea456 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/Makefile.in +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/Makefile.in @@ -49,6 +49,8 @@ LOCAL_INCLUDES = -I$(srcdir)/../../.. CPPSRCS = \ exception_handler.cc \ + minidump_generator.cc \ + linux_thread.cc \ $(NULL) # need static lib diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc index 1fe7e83d956..d336c1e268a 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc @@ -1,6 +1,8 @@ -// Copyright (c) 2009, Google Inc. +// Copyright (c) 2006, Google Inc. // All rights reserved. // +// Author: Li Liu +// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -27,88 +29,49 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// The ExceptionHandler object installs signal handlers for a number of -// signals. We rely on the signal handler running on the thread which crashed -// in order to identify it. This is true of the synchronous signals (SEGV etc), -// but not true of ABRT. Thus, if you send ABRT to yourself in a program which -// uses ExceptionHandler, you need to use tgkill to direct it to the current -// thread. -// -// The signal flow looks like this: -// -// SignalHandler (uses a global stack of ExceptionHandler objects to find -// | one to handle the signal. If the first rejects it, try -// | the second etc...) -// V -// HandleSignal ----------------------------| (clones a new process which -// | | shares an address space with -// (wait for cloned | the crashed process. This -// process) | allows us to ptrace the crashed -// | | process) -// V V -// (set signal handler to ThreadEntry (static function to bounce -// SIG_DFL and rethrow, | back into the object) -// killing the crashed | -// process) V -// DoDump (writes minidump) -// | -// V -// sys_exit -// - -// This code is a little fragmented. Different functions of the ExceptionHandler -// class run in a number of different contexts. Some of them run in a normal -// context and are easy to code, others run in a compromised context and the -// restrictions at the top of minidump_writer.cc apply: no libc and use the -// alternative malloc. Each function should have comment above it detailing the -// context which it runs in. - -#include "client/linux/handler/exception_handler.h" - -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include #include -#include "common/linux/linux_libc_support.h" -#include "common/linux/linux_syscall_support.h" -#include "common/linux/memory.h" -#include "client/linux/minidump_writer//minidump_writer.h" -#include "common/linux/guid_creator.h" +#include +#include +#include +#include +#include -// A wrapper for the tgkill syscall: send a signal to a specific thread. -static int tgkill(pid_t tgid, pid_t tid, int sig) { - syscall(__NR_tgkill, tgid, tid, sig); - return 0; -} +#include "client/linux/handler/exception_handler.h" +#include "common/linux/guid_creator.h" +#include "google_breakpad/common/minidump_format.h" namespace google_breakpad { -// The list of signals which we consider to be crashes. The default action for -// all these signals must be Core (see man 7 signal) because we rethrow the -// signal after handling it and expect that it'll be fatal. -static const int kExceptionSignals[] = { - SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, -1 +// Signals that we are interested. +int SigTable[] = { +#if defined(SIGSEGV) + SIGSEGV, +#endif +#ifdef SIGABRT + SIGABRT, +#endif +#ifdef SIGFPE + SIGFPE, +#endif +#ifdef SIGILL + SIGILL, +#endif +#ifdef SIGBUS + SIGBUS, +#endif }; -// We can stack multiple exception handlers. In that case, this is the global -// which holds the stack. -std::vector* ExceptionHandler::handler_stack_ = NULL; -unsigned ExceptionHandler::handler_stack_index_ = 0; +std::vector *ExceptionHandler::handler_stack_ = NULL; +int ExceptionHandler::handler_stack_index_ = 0; pthread_mutex_t ExceptionHandler::handler_stack_mutex_ = - PTHREAD_MUTEX_INITIALIZER; +PTHREAD_MUTEX_INITIALIZER; -// Runs before crashing: normal context. -ExceptionHandler::ExceptionHandler(const std::string &dump_path, +ExceptionHandler::ExceptionHandler(const string &dump_path, FilterCallback filter, MinidumpCallback callback, void *callback_context, @@ -117,77 +80,212 @@ ExceptionHandler::ExceptionHandler(const std::string &dump_path, callback_(callback), callback_context_(callback_context), dump_path_(), - handler_installed_(install_handler), - crash_handler_(NULL) { + installed_handler_(install_handler) { set_dump_path(dump_path); - if (install_handler) { - InstallHandlers(); + act_.sa_handler = HandleException; + act_.sa_flags = SA_ONSTACK; + sigemptyset(&act_.sa_mask); + // now, make sure we're blocking all the signals we are handling + // when we're handling any of them + for ( size_t i = 0; i < sizeof(SigTable) / sizeof(SigTable[0]); ++i) { + sigaddset(&act_.sa_mask, SigTable[i]); + } + if (install_handler) { + SetupHandler(); pthread_mutex_lock(&handler_stack_mutex_); - if (handler_stack_ == NULL) - handler_stack_ = new std::vector; - handler_stack_->push_back(this); + if (handler_stack_ == NULL) + handler_stack_ = new std::vector; + handler_stack_->push_back(this); pthread_mutex_unlock(&handler_stack_mutex_); } } -// Runs before crashing: normal context. ExceptionHandler::~ExceptionHandler() { - UninstallHandlers(); + TeardownAllHandler(); + pthread_mutex_lock(&handler_stack_mutex_); + if (handler_stack_->back() == this) { + handler_stack_->pop_back(); + } else { + fprintf(stderr, "warning: removing Breakpad handler out of order\n"); + for (std::vector::iterator iterator = + handler_stack_->begin(); + iterator != handler_stack_->end(); + ++iterator) { + if (*iterator == this) { + handler_stack_->erase(iterator); + } + } + } + + if (handler_stack_->empty()) { + // When destroying the last ExceptionHandler that installed a handler, + // clean up the handler stack. + delete handler_stack_; + handler_stack_ = NULL; + } + pthread_mutex_unlock(&handler_stack_mutex_); } -// Runs before crashing: normal context. -bool ExceptionHandler::InstallHandlers() { - // We run the signal handlers on an alternative stack because we might have - // crashed because of a stack overflow. +bool ExceptionHandler::WriteMinidump() { + bool success = InternalWriteMinidump(0, 0, NULL); + UpdateNextID(); + return success; +} - // We use this value rather than SIGSTKSZ because we would end up overrunning - // such a small stack. - static const unsigned kSigStackSize = 8192; +// static +bool ExceptionHandler::WriteMinidump(const string &dump_path, + MinidumpCallback callback, + void *callback_context) { + ExceptionHandler handler(dump_path, NULL, callback, + callback_context, false); + return handler.InternalWriteMinidump(0, 0, NULL); +} - signal_stack = malloc(kSigStackSize); - stack_t stack; - memset(&stack, 0, sizeof(stack)); - stack.ss_sp = signal_stack; - stack.ss_size = kSigStackSize; +void ExceptionHandler::SetupHandler() { + // Signal on a different stack to avoid using the stack + // of the crashing thread. + struct sigaltstack sig_stack; + sig_stack.ss_sp = malloc(MINSIGSTKSZ); + if (sig_stack.ss_sp == NULL) + return; + sig_stack.ss_size = MINSIGSTKSZ; + sig_stack.ss_flags = 0; - if (sigaltstack(&stack, NULL) == -1) + if (sigaltstack(&sig_stack, NULL) < 0) + return; + for (size_t i = 0; i < sizeof(SigTable) / sizeof(SigTable[0]); ++i) + SetupHandler(SigTable[i]); +} + +void ExceptionHandler::SetupHandler(int signo) { + + // We're storing pointers to the old signal action + // structure, rather than copying the structure + // because we can't count on the sa_mask field to + // be scalar. + struct sigaction *old_act = &old_actions_[signo]; + + if (sigaction(signo, &act_, old_act) < 0) + return; +} + +void ExceptionHandler::TeardownHandler(int signo) { + TeardownHandler(signo, NULL); +} + +void ExceptionHandler::TeardownHandler(int signo, struct sigaction *final_handler) { + if (old_actions_[signo].sa_handler) { + struct sigaction *act = &old_actions_[signo]; + sigaction(signo, act, final_handler); + memset(&old_actions_[signo], 0x0, sizeof(struct sigaction)); + } +} + +void ExceptionHandler::TeardownAllHandler() { + for (size_t i = 0; i < sizeof(SigTable) / sizeof(SigTable[0]); ++i) { + TeardownHandler(SigTable[i]); + } +} + +// static +void ExceptionHandler::HandleException(int signo) { + // In Linux, the context information about the signal is put on the stack of + // the signal handler frame as value parameter. For some reasons, the + // prototype of the handler doesn't declare this information as parameter, we + // will do it by hand. It is the second parameter above the signal number. + // However, if we are being called by another signal handler passing the + // signal up the chain, then we may not have this random extra parameter, + // so we may have to walk the stack to find it. We do the actual work + // on another thread, where it's a little safer, but we want the ebp + // from this frame to find it. + uintptr_t current_ebp = 0; + asm volatile ("movl %%ebp, %0" + :"=m"(current_ebp)); + + pthread_mutex_lock(&handler_stack_mutex_); + ExceptionHandler *current_handler = + handler_stack_->at(handler_stack_->size() - ++handler_stack_index_); + pthread_mutex_unlock(&handler_stack_mutex_); + + // Restore original handler. + struct sigaction old_action; + current_handler->TeardownHandler(signo, &old_action); + + struct sigcontext *sig_ctx = NULL; + if (current_handler->InternalWriteMinidump(signo, current_ebp, &sig_ctx)) { + // Fully handled this exception, safe to exit. + exit(EXIT_FAILURE); + } else { + // Exception not fully handled, will call the next handler in stack to + // process it. + if (old_action.sa_handler != NULL && sig_ctx != NULL) { + + // Have our own typedef, because of the comment above w.r.t signal + // context on the stack + typedef void (*SignalHandler)(int signo, struct sigcontext); + + SignalHandler old_handler = + reinterpret_cast(old_action.sa_handler); + + sigset_t old_set; + // Use SIG_BLOCK here because we don't want to unblock a signal + // that the signal handler we're currently in needs to block + sigprocmask(SIG_BLOCK, &old_action.sa_mask, &old_set); + old_handler(signo, *sig_ctx); + sigprocmask(SIG_SETMASK, &old_set, NULL); + } + + } + + pthread_mutex_lock(&handler_stack_mutex_); + current_handler->SetupHandler(signo); + --handler_stack_index_; + // All the handlers in stack have been invoked to handle the exception, + // normally the process should be terminated and should not reach here. + // In case we got here, ask the OS to handle it to avoid endless loop, + // normally the OS will generate a core and termiate the process. This + // may be desired to debug the program. + if (handler_stack_index_ == 0) + signal(signo, SIG_DFL); + pthread_mutex_unlock(&handler_stack_mutex_); +} + +bool ExceptionHandler::InternalWriteMinidump(int signo, + uintptr_t sighandler_ebp, + struct sigcontext **sig_ctx) { + if (filter_ && !filter_(callback_context_)) return false; - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sigemptyset(&sa.sa_mask); - - // mask all exception signals when we're handling one of them. - for (unsigned i = 0; kExceptionSignals[i] != -1; ++i) - sigaddset(&sa.sa_mask, kExceptionSignals[i]); - - sa.sa_sigaction = SignalHandler; - sa.sa_flags = SA_ONSTACK | SA_SIGINFO; - - for (unsigned i = 0; kExceptionSignals[i] != -1; ++i) { - struct sigaction* old = new struct sigaction; - if (sigaction(kExceptionSignals[i], &sa, old) == -1) - return false; - old_handlers_.push_back(std::make_pair(kExceptionSignals[i], old)); - } - return true; -} - -// Runs before crashing: normal context. -void ExceptionHandler::UninstallHandlers() { - for (unsigned i = 0; i < old_handlers_.size(); ++i) { - struct sigaction *action = - reinterpret_cast(old_handlers_[i].second); - sigaction(old_handlers_[i].first, action, NULL); - delete action; + bool success = false; + // Block all the signals we want to process when writting minidump. + // We don't want it to be interrupted. + sigset_t sig_blocked, sig_old; + bool blocked = true; + sigfillset(&sig_blocked); + for (size_t i = 0; i < sizeof(SigTable) / sizeof(SigTable[0]); ++i) + sigdelset(&sig_blocked, SigTable[i]); + if (sigprocmask(SIG_BLOCK, &sig_blocked, &sig_old) != 0) { + blocked = false; + fprintf(stderr, "google_breakpad::ExceptionHandler::HandleException: " + "failed to block signals.\n"); } - old_handlers_.clear(); + success = minidump_generator_.WriteMinidumpToFile( + next_minidump_path_c_, signo, sighandler_ebp, sig_ctx); + + // Unblock the signals. + if (blocked) { + sigprocmask(SIG_SETMASK, &sig_old, NULL); + } + + if (callback_) + success = callback_(dump_path_c_, next_minidump_id_c_, + callback_context_, success); + return success; } -// Runs before crashing: normal context. void ExceptionHandler::UpdateNextID() { GUID guid; char guid_str[kGUIDStringLength + 1]; @@ -205,120 +303,4 @@ void ExceptionHandler::UpdateNextID() { } } -// This function runs in a compromised context: see the top of the file. -// Runs on the crashing thread. -// static -void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) { - // All the exception signals are blocked at this point. - - pthread_mutex_lock(&handler_stack_mutex_); - - if (!handler_stack_->size()) { - pthread_mutex_unlock(&handler_stack_mutex_); - return; - } - - for (int i = handler_stack_->size() - 1; i >= 0; --i) { - if ((*handler_stack_)[i]->HandleSignal(sig, info, uc)) { - // successfully handled: We are in an invalid state since an exception - // signal has been delivered. We don't call the exit handlers because - // they could end up corrupting on-disk state. - break; - } - } - - pthread_mutex_unlock(&handler_stack_mutex_); - - // Terminate ourselves with the same signal so that our parent knows that we - // crashed. The default action for all the signals which we catch is Core, so - // this is the end of us. - signal(sig, SIG_DFL); - tgkill(getpid(), sys_gettid(), sig); - - // not reached. -} - -struct ThreadArgument { - pid_t pid; // the crashing process - ExceptionHandler* handler; - const void* context; // a CrashContext structure - size_t context_size; -}; - -// This is the entry function for the cloned process. We are in a compromised -// context here: see the top of the file. -// static -int ExceptionHandler::ThreadEntry(void *arg) { - const ThreadArgument *thread_arg = reinterpret_cast(arg); - return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context, - thread_arg->context_size) == false; -} - -// This function runs in a compromised context: see the top of the file. -// Runs on the crashing thread. -bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) { - if (filter_ && !filter_(callback_context_)) - return false; - - // Allow ourselves to be dumped. - sys_prctl(PR_SET_DUMPABLE, 1); - - CrashContext context; - memcpy(&context.siginfo, info, sizeof(siginfo_t)); - memcpy(&context.context, uc, sizeof(struct ucontext)); - memcpy(&context.float_state, ((struct ucontext *)uc)->uc_mcontext.fpregs, - sizeof(context.float_state)); - context.tid = sys_gettid(); - - if (crash_handler_ && crash_handler_(&context, sizeof(context), - callback_context_)) - return true; - - static const unsigned kChildStackSize = 8000; - PageAllocator allocator; - uint8_t* stack = (uint8_t*) allocator.Alloc(kChildStackSize); - if (!stack) - return false; - // clone() needs the top-most address. (scrub just to be safe) - stack += kChildStackSize; - my_memset(stack - 16, 0, 16); - - ThreadArgument thread_arg; - thread_arg.handler = this; - thread_arg.pid = getpid(); - thread_arg.context = &context; - thread_arg.context_size = sizeof(context); - - const pid_t child = sys_clone( - ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED, - &thread_arg, NULL, NULL, NULL); - int r, status; - do { - r = sys_waitpid(child, &status, __WALL); - } while (r == -1 && errno == EINTR); - - if (r == -1) { - static const char msg[] = "ExceptionHandler::HandleSignal: waitpid failed:"; - sys_write(2, msg, sizeof(msg) - 1); - sys_write(2, strerror(errno), strlen(strerror(errno))); - sys_write(2, "\n", 1); - } - - bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0; - - if (callback_) - success = callback_(dump_path_c_, next_minidump_id_c_, - callback_context_, success); - - return success; -} - -// This function runs in a compromised context: see the top of the file. -// Runs on the cloned process. -bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context, - size_t context_size) { - return google_breakpad::WriteMinidump( - next_minidump_path_c_, crashing_process, context, context_size); -} - } // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h index b579a6a9811..6ea09a11084 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h @@ -1,6 +1,8 @@ -// Copyright (c) 2009, Google Inc. +// Copyright (c) 2006, Google Inc. // All rights reserved. // +// Author: Li Liu +// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -27,16 +29,26 @@ // (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_HANDLER_EXCEPTION_HANDLER_H_ -#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ +#ifndef CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H__ +#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H__ -#include +#include + +#include #include - #include +#include + +#include "client/linux/handler/minidump_generator.h" + +// Context information when exception occured. +struct sigcontex; namespace google_breakpad { +using std::string; + +// // ExceptionHandler // // ExceptionHandler can write a minidump file when an exception occurs, @@ -61,6 +73,7 @@ namespace google_breakpad { // // Caller should try to make the callbacks as crash-friendly as possible, // it should avoid use heap memory allocation as much as possible. +// class ExceptionHandler { public: // A callback function to run before Breakpad performs any substantial @@ -95,15 +108,6 @@ class ExceptionHandler { void *context, bool succeeded); - // In certain cases, a user may wish to handle the generation of the minidump - // themselves. In this case, they can install a handler callback which is - // called when a crash has occured. If this function returns true, no other - // processing of occurs and the process will shortly be crashed. If this - // returns false, the normal processing continues. - typedef bool (*HandlerCallback)(const void* crash_context, - size_t crash_context_size, - void* context); - // Creates a new ExceptionHandler instance to handle writing minidumps. // Before writing a minidump, the optional filter callback will be called. // Its return value determines whether or not Breakpad should write a @@ -112,87 +116,111 @@ class ExceptionHandler { // If install_handler is true, then a minidump will be written whenever // an unhandled exception occurs. If it is false, minidumps will only // be written when WriteMinidump is called. - ExceptionHandler(const std::string &dump_path, + ExceptionHandler(const string &dump_path, FilterCallback filter, MinidumpCallback callback, void *callback_context, bool install_handler); ~ExceptionHandler(); // Get and set the minidump path. - std::string dump_path() const { return dump_path_; } - void set_dump_path(const std::string &dump_path) { + string dump_path() const { return dump_path_; } + void set_dump_path(const string &dump_path) { dump_path_ = dump_path; dump_path_c_ = dump_path_.c_str(); UpdateNextID(); } - void set_crash_handler(HandlerCallback callback) { - crash_handler_ = callback; - } - // Writes a minidump immediately. This can be used to capture the // execution state independently of a crash. Returns true on success. bool WriteMinidump(); // Convenience form of WriteMinidump which does not require an // ExceptionHandler instance. - static bool WriteMinidump(const std::string &dump_path, + static bool WriteMinidump(const string &dump_path, MinidumpCallback callback, void *callback_context); - // This structure is passed to minidump_writer.h:WriteMinidump via an opaque - // blob. It shouldn't be needed in any user code. - struct CrashContext { - siginfo_t siginfo; - pid_t tid; // the crashing thread. - struct ucontext context; - struct _libc_fpstate float_state; - }; + private: + // Setup crash handler. + void SetupHandler(); + // Setup signal handler for a signal. + void SetupHandler(int signo); + // Teardown the handler for a signal. + void TeardownHandler(int signo); + // Teardown the handler for a signal. + void TeardownHandler(int signo, struct sigaction *old); + // Teardown all handlers. + void TeardownAllHandler(); + + // Signal handler. + static void HandleException(int signo); + + // If called from a signal handler, sighandler_ebp is the ebp of + // that signal handler's frame, and sig_ctx is an out parameter + // that will be set to point at the sigcontext that was placed + // on the stack by the kernel. You can pass zero and NULL + // for the second and third parameters if you are not calling + // this from a signal handler. + bool InternalWriteMinidump(int signo, uintptr_t sighandler_ebp, + struct sigcontext **sig_ctx); + + // Generates a new ID and stores it in next_minidump_id, and stores the + // path of the next minidump to be written in next_minidump_path_. + void UpdateNextID(); private: - bool InstallHandlers(); - void UninstallHandlers(); - void PreresolveSymbols(); + FilterCallback filter_; + MinidumpCallback callback_; + void *callback_context_; - void UpdateNextID(); - static void SignalHandler(int sig, siginfo_t* info, void* uc); - bool HandleSignal(int sig, siginfo_t* info, void* uc); - static int ThreadEntry(void* arg); - bool DoDump(pid_t crashing_process, const void* context, - size_t context_size); + // The directory in which a minidump will be written, set by the dump_path + // argument to the constructor, or set_dump_path. + string dump_path_; - const FilterCallback filter_; - const MinidumpCallback callback_; - void* const callback_context_; + // The basename of the next minidump to be written, without the extension + string next_minidump_id_; - std::string dump_path_; - std::string next_minidump_path_; - std::string next_minidump_id_; + // The full pathname of the next minidump to be written, including the file + // extension + string next_minidump_path_; // Pointers to C-string representations of the above. These are set // when the above are set so we can avoid calling c_str during // an exception. - const char* dump_path_c_; - const char* next_minidump_path_c_; - const char* next_minidump_id_c_; + const char *dump_path_c_; + const char *next_minidump_id_c_; + const char *next_minidump_path_c_; - const bool handler_installed_; - void* signal_stack; // the handler stack. - HandlerCallback crash_handler_; + // True if the ExceptionHandler installed an unhandled exception filter + // when created (with an install_handler parameter set to true). + bool installed_handler_; // The global exception handler stack. This is need becuase there may exist // multiple ExceptionHandler instances in a process. Each will have itself // registered in this stack. - static std::vector *handler_stack_; + static std::vector *handler_stack_; // The index of the handler that should handle the next exception. - static unsigned handler_stack_index_; + static int handler_stack_index_; static pthread_mutex_t handler_stack_mutex_; - // A vector of the old signal handlers. The void* is a pointer to a newly - // allocated sigaction structure to avoid pulling in too many includes. - std::vector > old_handlers_; + // The minidump generator. + MinidumpGenerator minidump_generator_; + + // disallow copy ctor and operator= + explicit ExceptionHandler(const ExceptionHandler &); + void operator=(const ExceptionHandler &); + + // The sigactions structure we use for each signal + struct sigaction act_; + + + // Keep the previous handlers for the signal. + // We're wasting a bit of memory here since we only change + // the handler for some signals but i want to avoid allocating + // memory in the signal handler + struct sigaction old_actions_[NSIG]; }; } // namespace google_breakpad -#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ +#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_test.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_test.cc new file mode 100644 index 00000000000..2d94553ddfb --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_test.cc @@ -0,0 +1,124 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Author: Li Liu +// +// 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 +#include + +#include +#include +#include +#include + +#include "client/linux/handler/exception_handler.h" +#include "client/linux/handler/linux_thread.h" + +using namespace google_breakpad; + +// Thread use this to see if it should stop working. +static bool should_exit = false; + +static int foo2(int arg) { + // Stack variable, used for debugging stack dumps. + /*DDDebug*/printf("%s:%d\n", __FUNCTION__, __LINE__); + int c = 0xcccccccc; + fprintf(stderr, "Thread trying to crash: %x\n", getpid()); + c = *reinterpret_cast(0x5); + return c; +} + +static int foo(int arg) { + // Stack variable, used for debugging stack dumps. + int b = 0xbbbbbbbb; + b = foo2(b); + return b; +} + +static void *thread_crash(void *) { + // Stack variable, used for debugging stack dumps. + int a = 0xaaaaaaaa; + sleep(1); + a = foo(a); + printf("%x\n", a); + return NULL; +} + +static void *thread_main(void *) { + while (!should_exit) + sleep(1); + return NULL; +} + +static void CreateCrashThread() { + pthread_t h; + pthread_create(&h, NULL, thread_crash, NULL); + pthread_detach(h); +} + +// Create working threads. +static void CreateThread(int num) { + pthread_t h; + for (int i = 0; i < num; ++i) { + pthread_create(&h, NULL, thread_main, NULL); + pthread_detach(h); + } +} + +// Callback when minidump written. +static bool MinidumpCallback(const char *dump_path, + const char *minidump_id, + void *context, + bool succeeded) { + int index = reinterpret_cast(context); + printf("%d %s: %s is dumped\n", index, __FUNCTION__, minidump_id); + if (index == 0) { + should_exit = true; + return true; + } + // Don't process it. + return false; +} + +int main(int argc, char *argv[]) { + int handler_index = 0; + ExceptionHandler handler_ignore(".", NULL, MinidumpCallback, + (void*)handler_index, true); + ++handler_index; + ExceptionHandler handler_process(".", NULL, MinidumpCallback, + (void*)handler_index, true); + CreateCrashThread(); + CreateThread(10); + + while (true) + sleep(1); + should_exit = true; + + return 0; +} diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_unittest.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_unittest.cc deleted file mode 100644 index 2f4e10454f1..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_unittest.cc +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright (c) 2009, 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 - -#include -#include -#include -#include -#include -#include - -#include "client/linux/handler//exception_handler.h" -#include "client/linux/minidump_writer/minidump_writer.h" -#include "common/linux/linux_libc_support.h" -#include "common/linux/linux_syscall_support.h" -#include "breakpad_googletest_includes.h" - -// This provides a wrapper around system calls which may be -// interrupted by a signal and return EINTR. See man 7 signal. -#define HANDLE_EINTR(x) ({ \ - typeof(x) __eintr_result__; \ - do { \ - __eintr_result__ = x; \ - } while (__eintr_result__ == -1 && errno == EINTR); \ - __eintr_result__;\ -}) - -using namespace google_breakpad; - -static void sigchld_handler(int signo) { } - -class ExceptionHandlerTest : public ::testing::Test { - protected: - void SetUp() { - // We need to be able to wait for children, so SIGCHLD cannot be SIG_IGN. - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = sigchld_handler; - ASSERT_NE(sigaction(SIGCHLD, &sa, &old_action), -1); - } - - void TearDown() { - sigaction(SIGCHLD, &old_action, NULL); - } - - struct sigaction old_action; -}; - -TEST(ExceptionHandlerTest, Simple) { - ExceptionHandler handler("/tmp", NULL, NULL, NULL, true); -} - -static bool DoneCallback(const char* dump_path, - const char* minidump_id, - void* context, - bool succeeded) { - if (!succeeded) - return succeeded; - - int fd = (intptr_t) context; - uint32_t len = my_strlen(minidump_id); - HANDLE_EINTR(sys_write(fd, &len, sizeof(len))); - HANDLE_EINTR(sys_write(fd, minidump_id, len)); - sys_close(fd); - - return true; -} - -TEST(ExceptionHandlerTest, ChildCrash) { - int fds[2]; - ASSERT_NE(pipe(fds), -1); - - const pid_t child = fork(); - if (child == 0) { - close(fds[0]); - ExceptionHandler handler("/tmp", NULL, DoneCallback, (void*) fds[1], - true); - *reinterpret_cast(NULL) = 0; - } - close(fds[1]); - - int status; - ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); - ASSERT_TRUE(WIFSIGNALED(status)); - ASSERT_EQ(WTERMSIG(status), SIGSEGV); - - struct pollfd pfd; - memset(&pfd, 0, sizeof(pfd)); - pfd.fd = fds[0]; - pfd.events = POLLIN | POLLERR; - - const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); - ASSERT_EQ(r, 1); - ASSERT_TRUE(pfd.revents & POLLIN); - - uint32_t len; - ASSERT_EQ(read(fds[0], &len, sizeof(len)), sizeof(len)); - ASSERT_LT(len, 2048); - char* filename = reinterpret_cast(malloc(len + 1)); - ASSERT_EQ(read(fds[0], filename, len), len); - filename[len] = 0; - close(fds[0]); - - const std::string minidump_filename = std::string("/tmp/") + filename + - ".dmp"; - - struct stat st; - ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0); - ASSERT_GT(st.st_size, 0u); - unlink(minidump_filename.c_str()); -} - -static const unsigned kControlMsgSize = - CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred)); - -static bool -CrashHandler(const void* crash_context, size_t crash_context_size, - void* context) { - const int fd = (intptr_t) context; - int fds[2]; - pipe(fds); - - struct kernel_msghdr msg = {0}; - struct kernel_iovec iov; - iov.iov_base = const_cast(crash_context); - iov.iov_len = crash_context_size; - - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - char cmsg[kControlMsgSize]; - memset(cmsg, 0, kControlMsgSize); - msg.msg_control = cmsg; - msg.msg_controllen = sizeof(cmsg); - - struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); - hdr->cmsg_level = SOL_SOCKET; - hdr->cmsg_type = SCM_RIGHTS; - hdr->cmsg_len = CMSG_LEN(sizeof(int)); - *((int*) CMSG_DATA(hdr)) = fds[1]; - hdr = CMSG_NXTHDR((struct msghdr*) &msg, hdr); - hdr->cmsg_level = SOL_SOCKET; - hdr->cmsg_type = SCM_CREDENTIALS; - hdr->cmsg_len = CMSG_LEN(sizeof(struct ucred)); - struct ucred *cred = reinterpret_cast(CMSG_DATA(hdr)); - cred->uid = getuid(); - cred->gid = getgid(); - cred->pid = getpid(); - - HANDLE_EINTR(sys_sendmsg(fd, &msg, 0)); - sys_close(fds[1]); - - char b; - HANDLE_EINTR(sys_read(fds[0], &b, 1)); - - return true; -} - -TEST(ExceptionHandlerTest, ExternalDumper) { - int fds[2]; - ASSERT_NE(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds), -1); - static const int on = 1; - setsockopt(fds[0], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); - setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); - - const pid_t child = fork(); - if (child == 0) { - close(fds[0]); - ExceptionHandler handler("/tmp", NULL, NULL, (void*) fds[1], true); - handler.set_crash_handler(CrashHandler); - *reinterpret_cast(NULL) = 0; - } - - close(fds[1]); - struct msghdr msg = {0}; - struct iovec iov; - static const unsigned kCrashContextSize = - sizeof(ExceptionHandler::CrashContext); - char context[kCrashContextSize]; - char control[kControlMsgSize]; - iov.iov_base = context; - iov.iov_len = kCrashContextSize; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = control; - msg.msg_controllen = kControlMsgSize; - - const ssize_t n = HANDLE_EINTR(recvmsg(fds[0], &msg, 0)); - ASSERT_EQ(n, kCrashContextSize); - ASSERT_EQ(msg.msg_controllen, kControlMsgSize); - ASSERT_EQ(msg.msg_flags, 0); - - pid_t crashing_pid = -1; - int signal_fd = -1; - for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr; - hdr = CMSG_NXTHDR(&msg, hdr)) { - if (hdr->cmsg_level != SOL_SOCKET) - continue; - if (hdr->cmsg_type == SCM_RIGHTS) { - const unsigned len = hdr->cmsg_len - - (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr); - ASSERT_EQ(len, sizeof(int)); - signal_fd = *((int *) CMSG_DATA(hdr)); - } else if (hdr->cmsg_type == SCM_CREDENTIALS) { - const struct ucred *cred = - reinterpret_cast(CMSG_DATA(hdr)); - crashing_pid = cred->pid; - } - } - - ASSERT_NE(crashing_pid, -1); - ASSERT_NE(signal_fd, -1); - - char templ[] = "/tmp/exception-handler-unittest-XXXXXX"; - mktemp(templ); - ASSERT_TRUE(WriteMinidump(templ, crashing_pid, context, - kCrashContextSize)); - static const char b = 0; - HANDLE_EINTR(write(signal_fd, &b, 1)); - - int status; - ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); - ASSERT_TRUE(WIFSIGNALED(status)); - ASSERT_EQ(WTERMSIG(status), SIGSEGV); - - struct stat st; - ASSERT_EQ(stat(templ, &st), 0); - ASSERT_GT(st.st_size, 0u); - unlink(templ); -} diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.cc new file mode 100644 index 00000000000..c8ac492690d --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.cc @@ -0,0 +1,411 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Author: Li Liu +// +// 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "client/linux/handler/linux_thread.h" + +using namespace google_breakpad; + +// This unamed namespace contains helper function. +namespace { + +// Context information for the callbacks when validating address by listing +// modules. +struct AddressValidatingContext { + uintptr_t address; + bool is_mapped; + + AddressValidatingContext() : address(0UL), is_mapped(false) { + } +}; + +// Convert from string to int. +bool LocalAtoi(char *s, int *r) { + assert(s != NULL); + assert(r != NULL); + char *endptr = NULL; + int ret = strtol(s, &endptr, 10); + if (endptr == s) + return false; + *r = ret; + return true; +} + +// Fill the proc path of a thread given its id. +void FillProcPath(int pid, char *path, int path_size) { + char pid_str[32]; + snprintf(pid_str, sizeof(pid_str), "%d", pid); + snprintf(path, path_size, "/proc/%s/", pid_str); +} + +// Read thread info from /proc/$pid/status. +bool ReadThreadInfo(int pid, ThreadInfo *info) { + assert(info != NULL); + char status_path[80]; + // Max size we want to read from status file. + static const int kStatusMaxSize = 1024; + char status_content[kStatusMaxSize]; + + FillProcPath(pid, status_path, sizeof(status_path)); + strcat(status_path, "status"); + int fd = open(status_path, O_RDONLY, 0); + if (fd < 0) + return false; + + int num_read = read(fd, status_content, kStatusMaxSize - 1); + if (num_read < 0) { + close(fd); + return false; + } + close(fd); + status_content[num_read] = '\0'; + + char *tgid_start = strstr(status_content, "Tgid:"); + if (tgid_start) + sscanf(tgid_start, "Tgid:\t%d\n", &(info->tgid)); + else + // tgid not supported by kernel?? + info->tgid = 0; + + tgid_start = strstr(status_content, "Pid:"); + if (tgid_start) { + sscanf(tgid_start, "Pid:\t%d\n" "PPid:\t%d\n", &(info->pid), + &(info->ppid)); + return true; + } + return false; +} + +// Callback invoked for each mapped module. +// It use the module's adderss range to validate the address. +bool IsAddressInModuleCallback(const ModuleInfo &module_info, + void *context) { + AddressValidatingContext *addr = + reinterpret_cast(context); + addr->is_mapped = ((addr->address >= module_info.start_addr) && + (addr->address <= module_info.start_addr + + module_info.size)); + return !addr->is_mapped; +} + +#if defined(__i386__) && !defined(NO_FRAME_POINTER) +void *GetNextFrame(void **last_ebp) { + void *sp = *last_ebp; + if ((unsigned long)sp == (unsigned long)last_ebp) + return NULL; + if ((unsigned long)sp & (sizeof(void *) - 1)) + return NULL; + if ((unsigned long)sp - (unsigned long)last_ebp > 100000) + return NULL; + return sp; +} +#else +void *GetNextFrame(void **last_ebp) { + return reinterpret_cast(last_ebp); +} +#endif + +// Suspend a thread by attaching to it. +bool SuspendThread(int pid, void *context) { + // This may fail if the thread has just died or debugged. + errno = 0; + if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 && + errno != 0) { + return false; + } + while (waitpid(pid, NULL, __WALL) < 0) { + if (errno != EINTR) { + ptrace(PTRACE_DETACH, pid, NULL, NULL); + return false; + } + } + return true; +} + +// Resume a thread by detaching from it. +bool ResumeThread(int pid, void *context) { + return ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0; +} + +// Callback to get the thread information. +// Will be called for each thread found. +bool ThreadInfoCallback(int pid, void *context) { + CallbackParam *thread_callback = + reinterpret_cast *>(context); + ThreadInfo thread_info; + if (ReadThreadInfo(pid, &thread_info) && thread_callback) { + // Invoke callback from caller. + return (thread_callback->call_back)(thread_info, thread_callback->context); + } + return false; +} + +} // namespace + +namespace google_breakpad { + +LinuxThread::LinuxThread(int pid) : pid_(pid) , threads_suspened_(false) { +} + +LinuxThread::~LinuxThread() { + if (threads_suspened_) + ResumeAllThreads(); +} + +int LinuxThread::SuspendAllThreads() { + CallbackParam callback_param(SuspendThread, NULL); + int thread_count = 0; + if ((thread_count = IterateProcSelfTask(pid_, &callback_param)) > 0) + threads_suspened_ = true; + return thread_count; +} + +void LinuxThread::ResumeAllThreads() const { + CallbackParam callback_param(ResumeThread, NULL); + IterateProcSelfTask(pid_, &callback_param); +} + +int LinuxThread::GetThreadCount() const { + return IterateProcSelfTask(pid_, NULL); +} + +int LinuxThread::ListThreads( + CallbackParam *thread_callback_param) const { + CallbackParam callback_param(ThreadInfoCallback, + thread_callback_param); + return IterateProcSelfTask(pid_, &callback_param); +} + +bool LinuxThread::GetRegisters(int pid, user_regs_struct *regs) const { + assert(regs); + return (regs != NULL && + (ptrace(PTRACE_GETREGS, pid, NULL, regs) == 0) && + errno == 0); +} + +// Get the floating-point registers of a thread. +// The caller must get the thread pid by ListThreads. +bool LinuxThread::GetFPRegisters(int pid, user_fpregs_struct *regs) const { + assert(regs); + return (regs != NULL && + (ptrace(PTRACE_GETREGS, pid, NULL, regs) ==0) && + errno == 0); +} + +bool LinuxThread::GetFPXRegisters(int pid, user_fpxregs_struct *regs) const { + assert(regs); + return (regs != NULL && + (ptrace(PTRACE_GETFPREGS, pid, NULL, regs) != 0) && + errno == 0); +} + +bool LinuxThread::GetDebugRegisters(int pid, DebugRegs *regs) const { + assert(regs); + +#define GET_DR(name, num)\ + name->dr##num = ptrace(PTRACE_PEEKUSER, pid,\ + offsetof(struct user, u_debugreg[num]), NULL) + GET_DR(regs, 0); + GET_DR(regs, 1); + GET_DR(regs, 2); + GET_DR(regs, 3); + GET_DR(regs, 4); + GET_DR(regs, 5); + GET_DR(regs, 6); + GET_DR(regs, 7); + return true; +} + +int LinuxThread::GetThreadStackDump(uintptr_t current_ebp, + uintptr_t current_esp, + void *buf, + int buf_size) const { + assert(buf); + assert(buf_size > 0); + + uintptr_t stack_bottom = GetThreadStackBottom(current_ebp); + int size = stack_bottom - current_esp; + size = buf_size > size ? size : buf_size; + if (size > 0) + memcpy(buf, reinterpret_cast(current_esp), size); + return size; +} + +// Get the stack bottom of a thread by stack walking. It works +// unless the stack has been corrupted or the frame pointer has been omited. +// This is just a temporary solution before we get better ideas about how +// this can be done. +// +// We will check each frame address by checking into module maps. +// TODO(liuli): Improve it. +uintptr_t LinuxThread::GetThreadStackBottom(uintptr_t current_ebp) const { + void **sp = reinterpret_cast(current_ebp); + void **previous_sp = sp; + while (sp && IsAddressMapped((uintptr_t)sp)) { + previous_sp = sp; + sp = reinterpret_cast(GetNextFrame(sp)); + } + return (uintptr_t)previous_sp; +} + +int LinuxThread::GetModuleCount() const { + return ListModules(NULL); +} + +int LinuxThread::ListModules( + CallbackParam *callback_param) const { + char line[512]; + const char *maps_path = "/proc/self/maps"; + + int module_count = 0; + FILE *fp = fopen(maps_path, "r"); + if (fp == NULL) + return -1; + + uintptr_t start_addr; + uintptr_t end_addr; + while (fgets(line, sizeof(line), fp) != NULL) { + if (sscanf(line, "%x-%x", &start_addr, &end_addr) == 2) { + ModuleInfo module; + memset(&module, 0, sizeof(module)); + module.start_addr = start_addr; + module.size = end_addr - start_addr; + char *name = NULL; + assert(module.size > 0); + // Only copy name if the name is a valid path name. + if ((name = strchr(line, '/')) != NULL) { + // Get rid of the last '\n' in line + char *last_return = strchr(line, '\n'); + if (last_return != NULL) + *last_return = '\0'; + // Keep a space for the ending 0. + strncpy(module.name, name, sizeof(module.name) - 1); + ++module_count; + } + if (callback_param && + !(callback_param->call_back(module, callback_param->context))) + break; + } + } + fclose(fp); + return module_count; +} + +// Parse /proc/$pid/tasks to list all the threads of the process identified by +// pid. +int LinuxThread::IterateProcSelfTask(int pid, + CallbackParam *callback_param) const { + char task_path[80]; + FillProcPath(pid, task_path, sizeof(task_path)); + strcat(task_path, "task"); + + DIR *dir = opendir(task_path); + if (dir == NULL) + return -1; + + int pid_number = 0; + // Record the last pid we've found. This is used for duplicated thread + // removal. Duplicated thread information can be found in /proc/$pid/tasks. + int last_pid = -1; + struct dirent *entry = NULL; + while ((entry = readdir(dir)) != NULL) { + if (strcmp(entry->d_name, ".") && + strcmp(entry->d_name, "..")) { + int tpid = 0; + if (LocalAtoi(entry->d_name, &tpid) && + last_pid != tpid) { + last_pid = tpid; + ++pid_number; + // Invoke the callback. + if (callback_param && + !(callback_param->call_back)(tpid, callback_param->context)) + break; + } + } + } + closedir(dir); + return pid_number; +} + +// Check if the address is a valid virtual address. +// If the address is in any of the mapped modules, we take it as valid. +// Otherwise it is invalid. +bool LinuxThread::IsAddressMapped(uintptr_t address) const { + AddressValidatingContext addr; + addr.address = address; + CallbackParam callback_param(IsAddressInModuleCallback, + &addr); + ListModules(&callback_param); + return addr.is_mapped; +} + +bool LinuxThread::FindSigContext(uintptr_t sighandler_ebp, + struct sigcontext **sig_ctx) { + uintptr_t previous_ebp; + const int MAX_STACK_DEPTH = 10; + int depth_counter = 0; + + do { + // We're looking for a |struct sigcontext| as the second parameter + // to a signal handler function call. Luckily, the sigcontext + // has an ebp member which should match the ebp pointed to + // by the ebp of the signal handler frame. + previous_ebp = reinterpret_cast(GetNextFrame( + reinterpret_cast(sighandler_ebp))); + // The stack looks like this: + // | previous ebp | previous eip | first param | second param |, + // so we need to offset by 3 to get to the second parameter. + *sig_ctx = reinterpret_cast(sighandler_ebp + + 3 * sizeof(uintptr_t)); + sighandler_ebp = previous_ebp; + depth_counter++; + } while(previous_ebp != (*sig_ctx)->ebp && sighandler_ebp != 0 && + IsAddressMapped(sighandler_ebp) && depth_counter < MAX_STACK_DEPTH); + + return previous_ebp == (*sig_ctx)->ebp && previous_ebp != 0; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.h b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.h new file mode 100644 index 00000000000..f738c2e0ba5 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.h @@ -0,0 +1,204 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Author: Li Liu +// +// 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_HANDLER_LINUX_THREAD_H__ +#define CLIENT_LINUX_HANDLER_LINUX_THREAD_H__ + +#include +#include + +namespace google_breakpad { + +// Max module path name length. +#define kMaxModuleNameLength 256 + +// Holding information about a thread in the process. +struct ThreadInfo { + // Id of the thread group. + int tgid; + // Id of the thread. + int pid; + // Id of the parent process. + int ppid; +}; + +// Holding infomaton about a module in the process. +struct ModuleInfo { + char name[kMaxModuleNameLength]; + uintptr_t start_addr; + int size; +}; + +// Holding debug registers. +struct DebugRegs { + int dr0; + int dr1; + int dr2; + int dr3; + int dr4; + int dr5; + int dr6; + int dr7; +}; + +// A callback to run when got a thread in the process. +// Return true will go on to the next thread while return false will stop the +// iteration. +typedef bool (*ThreadCallback)(const ThreadInfo &thread_info, void *context); + +// A callback to run when a new module is found in the process. +// Return true will go on to the next module while return false will stop the +// iteration. +typedef bool (*ModuleCallback)(const ModuleInfo &module_info, void *context); + +// Holding the callback information. +template +struct CallbackParam { + // Callback function address. + CallbackFunc call_back; + // Callback context; + void *context; + + CallbackParam() : call_back(NULL), context(NULL) { + } + + CallbackParam(CallbackFunc func, void *func_context) : + call_back(func), context(func_context) { + } +}; + +/////////////////////////////////////////////////////////////////////////////// + +// +// LinuxThread +// +// Provides handy support for operation on linux threads. +// It uses ptrace to get thread registers. Since ptrace only works in a +// different process other than the one being ptraced, user of this class +// should create another process before using the class. +// +// The process should be created in the following way: +// int cloned_pid = clone(ProcessEntryFunction, stack_address, +// CLONE_VM | CLONE_FILES | CLONE_FS | CLONE_UNTRACED, +// (void*)&arguments); +// waitpid(cloned_pid, NULL, __WALL); +// +// If CLONE_VM is not used, GetThreadStackBottom, GetThreadStackDump +// will not work since it just use memcpy to get the stack dump. +// +class LinuxThread { + public: + // Create a LinuxThread instance to list all the threads in a process. + explicit LinuxThread(int pid); + ~LinuxThread(); + + // Stop all the threads in the process. + // Return the number of stopped threads in the process. + // Return -1 means failed to stop threads. + int SuspendAllThreads(); + + // Resume all the suspended threads. + void ResumeAllThreads() const; + + // Get the count of threads in the process. + // Return -1 means error. + int GetThreadCount() const; + + // List the threads of process. + // Whenever there is a thread found, the callback will be invoked to process + // the information. + // Return number of threads listed. + int ListThreads(CallbackParam *thread_callback_param) const; + + // Get the general purpose registers of a thread. + // The caller must get the thread pid by ListThreads. + bool GetRegisters(int pid, user_regs_struct *regs) const; + + // Get the floating-point registers of a thread. + // The caller must get the thread pid by ListThreads. + bool GetFPRegisters(int pid, user_fpregs_struct *regs) const; + + // Get all the extended floating-point registers. May not work on all + // machines. + // The caller must get the thread pid by ListThreads. + bool GetFPXRegisters(int pid, user_fpxregs_struct *regs) const; + + // Get the debug registers. + // The caller must get the thread pid by ListThreads. + bool GetDebugRegisters(int pid, DebugRegs *regs) const; + + // Get the stack memory dump. + int GetThreadStackDump(uintptr_t current_ebp, + uintptr_t current_esp, + void *buf, + int buf_size) const; + + // Get the module count of the current process. + int GetModuleCount() const; + + // Get the mapped modules in the address space. + // Whenever a module is found, the callback will be invoked to process the + // information. + // Return how may modules are found. + int ListModules(CallbackParam *callback_param) const; + + // Get the bottom of the stack from ebp. + uintptr_t GetThreadStackBottom(uintptr_t current_ebp) const; + + // Finds a sigcontext on the stack given the ebp of our signal handler. + bool FindSigContext(uintptr_t sighandler_ebp, struct sigcontext **sig_ctx); + + private: + // This callback will run when a new thread has been found. + typedef bool (*PidCallback)(int pid, void *context); + + // Read thread information from /proc/$pid/task. + // Whenever a thread has been found, and callback will be invoked with + // the pid of the thread. + // Return number of threads found. + // Return -1 means the directory doesn't exist. + int IterateProcSelfTask(int pid, + CallbackParam *callback_param) const; + + // Check if the address is a valid virtual address. + bool IsAddressMapped(uintptr_t address) const; + + private: + // The pid of the process we are listing threads. + int pid_; + + // Mark if we have suspended the threads. + bool threads_suspened_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_HANDLER_LINUX_THREAD_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread_test.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread_test.cc new file mode 100644 index 00000000000..aeb5e64c90b --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread_test.cc @@ -0,0 +1,224 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Author: Li Liu +// +// 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 +#include +#include +#include + +#include +#include +#include + +#include "client/linux/handler/linux_thread.h" + +using namespace google_breakpad; + +// Thread use this to see if it should stop working. +static bool should_exit = false; + +static void foo2(int *a) { + // Stack variable, used for debugging stack dumps. + int c = 0xcccccccc; + c = c; + while (!should_exit) + sleep(1); +} + +static void foo() { + // Stack variable, used for debugging stack dumps. + int a = 0xaaaaaaaa; + foo2(&a); +} + +static void *thread_main(void *) { + // Stack variable, used for debugging stack dumps. + int b = 0xbbbbbbbb; + b = b; + while (!should_exit) { + foo(); + } + return NULL; +} + +static void CreateThreads(int num) { + pthread_t handle; + for (int i = 0; i < num; i++) { + if (0 != pthread_create(&handle, NULL, thread_main, NULL)) + fprintf(stderr, "Failed to create thread.\n"); + else + pthread_detach(handle); + } +} + +static bool ProcessOneModule(const struct ModuleInfo &module_info, + void *context) { + printf("0x%x[%8d] %s\n", module_info.start_addr, module_info.size, + module_info.name); + return true; +} + +static bool ProcessOneThread(const struct ThreadInfo &thread_info, + void *context) { + printf("\n\nPID: %d, TGID: %d, PPID: %d\n", + thread_info.pid, + thread_info.tgid, + thread_info.ppid); + + struct user_regs_struct regs; + struct user_fpregs_struct fp_regs; + struct user_fpxregs_struct fpx_regs; + struct DebugRegs dbg_regs; + + LinuxThread *threads = reinterpret_cast(context); + memset(®s, 0, sizeof(regs)); + if (threads->GetRegisters(thread_info.pid, ®s)) { + printf(" gs = 0x%lx\n", regs.xgs); + printf(" fs = 0x%lx\n", regs.xfs); + printf(" es = 0x%lx\n", regs.xes); + printf(" ds = 0x%lx\n", regs.xds); + printf(" edi = 0x%lx\n", regs.edi); + printf(" esi = 0x%lx\n", regs.esi); + printf(" ebx = 0x%lx\n", regs.ebx); + printf(" edx = 0x%lx\n", regs.edx); + printf(" ecx = 0x%lx\n", regs.ecx); + printf(" eax = 0x%lx\n", regs.eax); + printf(" ebp = 0x%lx\n", regs.ebp); + printf(" eip = 0x%lx\n", regs.eip); + printf(" cs = 0x%lx\n", regs.xcs); + printf(" eflags = 0x%lx\n", regs.eflags); + printf(" esp = 0x%lx\n", regs.esp); + printf(" ss = 0x%lx\n", regs.xss); + } else { + fprintf(stderr, "ERROR: Failed to get general purpose registers\n"); + } + memset(&fp_regs, 0, sizeof(fp_regs)); + if (threads->GetFPRegisters(thread_info.pid, &fp_regs)) { + printf("\n Floating point registers:\n"); + printf(" fctl = 0x%lx\n", fp_regs.cwd); + printf(" fstat = 0x%lx\n", fp_regs.swd); + printf(" ftag = 0x%lx\n", fp_regs.twd); + printf(" fioff = 0x%lx\n", fp_regs.fip); + printf(" fiseg = 0x%lx\n", fp_regs.fcs); + printf(" fooff = 0x%lx\n", fp_regs.foo); + printf(" foseg = 0x%lx\n", fp_regs.fos); + int st_space_size = sizeof(fp_regs.st_space) / sizeof(fp_regs.st_space[0]); + printf(" st_space[%2d] = 0x", st_space_size); + for (int i = 0; i < st_space_size; ++i) + printf("%02lx", fp_regs.st_space[i]); + printf("\n"); + } else { + fprintf(stderr, "ERROR: Failed to get floating-point registers\n"); + } + memset(&fpx_regs, 0, sizeof(fpx_regs)); + if (threads->GetFPXRegisters(thread_info.pid, &fpx_regs)) { + printf("\n Extended floating point registers:\n"); + printf(" fctl = 0x%x\n", fpx_regs.cwd); + printf(" fstat = 0x%x\n", fpx_regs.swd); + printf(" ftag = 0x%x\n", fpx_regs.twd); + printf(" fioff = 0x%lx\n", fpx_regs.fip); + printf(" fiseg = 0x%lx\n", fpx_regs.fcs); + printf(" fooff = 0x%lx\n", fpx_regs.foo); + printf(" foseg = 0x%lx\n", fpx_regs.fos); + printf(" fop = 0x%x\n", fpx_regs.fop); + printf(" mxcsr = 0x%lx\n", fpx_regs.mxcsr); + int space_size = sizeof(fpx_regs.st_space) / sizeof(fpx_regs.st_space[0]); + printf(" st_space[%2d] = 0x", space_size); + for (int i = 0; i < space_size; ++i) + printf("%02lx", fpx_regs.st_space[i]); + printf("\n"); + space_size = sizeof(fpx_regs.xmm_space) / sizeof(fpx_regs.xmm_space[0]); + printf(" xmm_space[%2d] = 0x", space_size); + for (int i = 0; i < space_size; ++i) + printf("%02lx", fpx_regs.xmm_space[i]); + printf("\n"); + } + if (threads->GetDebugRegisters(thread_info.pid, &dbg_regs)) { + printf("\n Debug registers:\n"); + printf(" dr0 = 0x%x\n", dbg_regs.dr0); + printf(" dr1 = 0x%x\n", dbg_regs.dr1); + printf(" dr2 = 0x%x\n", dbg_regs.dr2); + printf(" dr3 = 0x%x\n", dbg_regs.dr3); + printf(" dr4 = 0x%x\n", dbg_regs.dr4); + printf(" dr5 = 0x%x\n", dbg_regs.dr5); + printf(" dr6 = 0x%x\n", dbg_regs.dr6); + printf(" dr7 = 0x%x\n", dbg_regs.dr7); + printf("\n"); + } + if (regs.esp != 0) { + // Print the stack content. + int size = 1024 * 2; + char *buf = new char[size]; + size = threads->GetThreadStackDump(regs.ebp, + regs.esp, + (void*)buf, size); + printf(" Stack content: = 0x"); + size /= sizeof(unsigned long); + unsigned long *p_buf = (unsigned long *)(buf); + for (int i = 0; i < size; i += 1) + printf("%.8lx ", p_buf[i]); + delete []buf; + printf("\n"); + } + return true; +} + +static int PrintAllThreads(void *argument) { + int pid = (int)argument; + + LinuxThread threads(pid); + int total_thread = threads.SuspendAllThreads(); + printf("There are %d threads in the process: %d\n", total_thread, pid); + int total_module = threads.GetModuleCount(); + printf("There are %d modules in the process: %d\n", total_module, pid); + CallbackParam module_callback(ProcessOneModule, &threads); + threads.ListModules(&module_callback); + CallbackParam thread_callback(ProcessOneThread, &threads); + threads.ListThreads(&thread_callback); + return 0; +} + +int main(int argc, char **argv) { + int pid = getpid(); + printf("Main thread is %d\n", pid); + CreateThreads(1); + // Create stack for the process. + char *stack = new char[1024 * 100]; + int cloned_pid = clone(PrintAllThreads, stack + 1024 * 100, + CLONE_VM | CLONE_FILES | CLONE_FS | CLONE_UNTRACED, + (void*)getpid()); + waitpid(cloned_pid, NULL, __WALL); + should_exit = true; + printf("Test finished.\n"); + + delete []stack; + return 0; +} diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.cc new file mode 100644 index 00000000000..d172092c89e --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.cc @@ -0,0 +1,816 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Author: Li Liu +// +// 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "common/linux/file_id.h" +#include "client/linux/handler/linux_thread.h" +#include "client/minidump_file_writer.h" +#include "client/minidump_file_writer-inl.h" +#include "google_breakpad/common/minidump_format.h" +#include "client/linux/handler/minidump_generator.h" + +#ifndef CLONE_UNTRACED +#define CLONE_UNTRACED 0x00800000 +#endif + +// This unnamed namespace contains helper functions. +namespace { + +using namespace google_breakpad; + +// Argument for the writer function. +struct WriterArgument { + MinidumpFileWriter *minidump_writer; + + // Context for the callback. + void *version_context; + + // Pid of the thread who called WriteMinidumpToFile + int requester_pid; + + // The stack bottom of the thread which caused the dump. + // Mainly used to find the thread id of the crashed thread since signal + // handler may not be called in the thread who caused it. + uintptr_t crashed_stack_bottom; + + // Pid of the crashing thread. + int crashed_pid; + + // Signal number when crash happed. Can be 0 if this is a requested dump. + int signo; + + // The ebp of the signal handler frame. Can be zero if this + // is a requested dump. + uintptr_t sighandler_ebp; + + // Signal context when crash happed. Can be NULL if this is a requested dump. + // This is actually an out parameter, but it will be filled in at the start + // of the writer thread. + struct sigcontext *sig_ctx; + + // Used to get information about the threads. + LinuxThread *thread_lister; +}; + +// Holding context information for the callback of finding the crashing thread. +struct FindCrashThreadContext { + const LinuxThread *thread_lister; + uintptr_t crashing_stack_bottom; + int crashing_thread_pid; + + FindCrashThreadContext() : + thread_lister(NULL), + crashing_stack_bottom(0UL), + crashing_thread_pid(-1) { + } +}; + +// Callback for list threads. +// It will compare the stack bottom of the provided thread with the stack +// bottom of the crashed thread, it they are eqaul, this is thread is the one +// who crashed. +bool IsThreadCrashedCallback(const ThreadInfo &thread_info, void *context) { + FindCrashThreadContext *crashing_context = + static_cast(context); + const LinuxThread *thread_lister = crashing_context->thread_lister; + struct user_regs_struct regs; + if (thread_lister->GetRegisters(thread_info.pid, ®s)) { + uintptr_t last_ebp = regs.ebp; + uintptr_t stack_bottom = thread_lister->GetThreadStackBottom(last_ebp); + if (stack_bottom > last_ebp && + stack_bottom == crashing_context->crashing_stack_bottom) { + // Got it. Stop iteration. + crashing_context->crashing_thread_pid = thread_info.pid; + return false; + } + } + return true; +} + +// Find the crashing thread id. +// This is done based on stack bottom comparing. +int FindCrashingThread(uintptr_t crashing_stack_bottom, + int requester_pid, + const LinuxThread *thread_lister) { + FindCrashThreadContext context; + context.thread_lister = thread_lister; + context.crashing_stack_bottom = crashing_stack_bottom; + CallbackParam callback_param(IsThreadCrashedCallback, + &context); + thread_lister->ListThreads(&callback_param); + return context.crashing_thread_pid; +} + +// Write the thread stack info minidump. +bool WriteThreadStack(uintptr_t last_ebp, + uintptr_t last_esp, + const LinuxThread *thread_lister, + UntypedMDRVA *memory, + MDMemoryDescriptor *loc) { + // Maximum stack size for a thread. + uintptr_t stack_bottom = thread_lister->GetThreadStackBottom(last_ebp); + if (stack_bottom > last_esp) { + int size = stack_bottom - last_esp; + if (size > 0) { + if (!memory->Allocate(size)) + return false; + memory->Copy(reinterpret_cast(last_esp), size); + loc->start_of_memory_range = 0 | last_esp; + loc->memory = memory->location(); + } + return true; + } + return false; +} + +// Write CPU context based on signal context. +bool WriteContext(MDRawContextX86 *context, const struct sigcontext *sig_ctx, + const DebugRegs *debug_regs) { + assert(sig_ctx != NULL); + context->context_flags = MD_CONTEXT_X86_FULL; + context->gs = sig_ctx->gs; + context->fs = sig_ctx->fs; + context->es = sig_ctx->es; + context->ds = sig_ctx->ds; + context->cs = sig_ctx->cs; + context->ss = sig_ctx->ss; + context->edi = sig_ctx->edi; + context->esi = sig_ctx->esi; + context->ebp = sig_ctx->ebp; + context->esp = sig_ctx->esp; + context->ebx = sig_ctx->ebx; + context->edx = sig_ctx->edx; + context->ecx = sig_ctx->ecx; + context->eax = sig_ctx->eax; + context->eip = sig_ctx->eip; + context->eflags = sig_ctx->eflags; + if (sig_ctx->fpstate != NULL) { + context->context_flags = MD_CONTEXT_X86_FULL | + MD_CONTEXT_X86_FLOATING_POINT; + context->float_save.control_word = sig_ctx->fpstate->cw; + context->float_save.status_word = sig_ctx->fpstate->sw; + context->float_save.tag_word = sig_ctx->fpstate->tag; + context->float_save.error_offset = sig_ctx->fpstate->ipoff; + context->float_save.error_selector = sig_ctx->fpstate->cssel; + context->float_save.data_offset = sig_ctx->fpstate->dataoff; + context->float_save.data_selector = sig_ctx->fpstate->datasel; + memcpy(context->float_save.register_area, sig_ctx->fpstate->_st, + sizeof(context->float_save.register_area)); + } + + if (debug_regs != NULL) { + context->context_flags |= MD_CONTEXT_X86_DEBUG_REGISTERS; + context->dr0 = debug_regs->dr0; + context->dr1 = debug_regs->dr1; + context->dr2 = debug_regs->dr2; + context->dr3 = debug_regs->dr3; + context->dr6 = debug_regs->dr6; + context->dr7 = debug_regs->dr7; + } + return true; +} + +// Write CPU context based on provided registers. +bool WriteContext(MDRawContextX86 *context, + const struct user_regs_struct *regs, + const struct user_fpregs_struct *fp_regs, + const DebugRegs *dbg_regs) { + if (!context || !regs) + return false; + + context->context_flags = MD_CONTEXT_X86_FULL; + + context->cs = regs->xcs; + context->ds = regs->xds; + context->es = regs->xes; + context->fs = regs->xfs; + context->gs = regs->xgs; + context->ss = regs->xss; + context->edi = regs->edi; + context->esi = regs->esi; + context->ebx = regs->ebx; + context->edx = regs->edx; + context->ecx = regs->ecx; + context->eax = regs->eax; + context->ebp = regs->ebp; + context->eip = regs->eip; + context->esp = regs->esp; + context->eflags = regs->eflags; + + if (dbg_regs != NULL) { + context->context_flags |= MD_CONTEXT_X86_DEBUG_REGISTERS; + context->dr0 = dbg_regs->dr0; + context->dr1 = dbg_regs->dr1; + context->dr2 = dbg_regs->dr2; + context->dr3 = dbg_regs->dr3; + context->dr6 = dbg_regs->dr6; + context->dr7 = dbg_regs->dr7; + } + + if (fp_regs != NULL) { + context->context_flags |= MD_CONTEXT_X86_FLOATING_POINT; + context->float_save.control_word = fp_regs->cwd; + context->float_save.status_word = fp_regs->swd; + context->float_save.tag_word = fp_regs->twd; + context->float_save.error_offset = fp_regs->fip; + context->float_save.error_selector = fp_regs->fcs; + context->float_save.data_offset = fp_regs->foo; + context->float_save.data_selector = fp_regs->fos; + context->float_save.data_selector = fp_regs->fos; + + memcpy(context->float_save.register_area, fp_regs->st_space, + sizeof(context->float_save.register_area)); + } + return true; +} + +// Write information about a crashed thread. +// When a thread crash, kernel will write something on the stack for processing +// signal. This makes the current stack not reliable, and our stack walker +// won't figure out the whole call stack for this. So we write the stack at the +// time of the crash into the minidump file, not the current stack. +bool WriteCrashedThreadStream(MinidumpFileWriter *minidump_writer, + const WriterArgument *writer_args, + const ThreadInfo &thread_info, + MDRawThread *thread) { + assert(writer_args->sig_ctx != NULL); + + thread->thread_id = thread_info.pid; + + UntypedMDRVA memory(minidump_writer); + if (!WriteThreadStack(writer_args->sig_ctx->ebp, + writer_args->sig_ctx->esp, + writer_args->thread_lister, + &memory, + &thread->stack)) + return false; + + TypedMDRVA context(minidump_writer); + if (!context.Allocate()) + return false; + thread->thread_context = context.location(); + memset(context.get(), 0, sizeof(MDRawContextX86)); + return WriteContext(context.get(), writer_args->sig_ctx, NULL); +} + +// Write information about a thread. +// This function only processes thread running normally at the crash. +bool WriteThreadStream(MinidumpFileWriter *minidump_writer, + const LinuxThread *thread_lister, + const ThreadInfo &thread_info, + MDRawThread *thread) { + thread->thread_id = thread_info.pid; + + struct user_regs_struct regs; + memset(®s, 0, sizeof(regs)); + if (!thread_lister->GetRegisters(thread_info.pid, ®s)) { + perror(NULL); + return false; + } + + UntypedMDRVA memory(minidump_writer); + if (!WriteThreadStack(regs.ebp, + regs.esp, + thread_lister, + &memory, + &thread->stack)) + return false; + + struct user_fpregs_struct fp_regs; + DebugRegs dbg_regs; + memset(&fp_regs, 0, sizeof(fp_regs)); + // Get all the registers. + thread_lister->GetFPRegisters(thread_info.pid, &fp_regs); + thread_lister->GetDebugRegisters(thread_info.pid, &dbg_regs); + + // Write context + TypedMDRVA context(minidump_writer); + if (!context.Allocate()) + return false; + thread->thread_context = context.location(); + memset(context.get(), 0, sizeof(MDRawContextX86)); + return WriteContext(context.get(), ®s, &fp_regs, &dbg_regs); +} + +bool WriteCPUInformation(MDRawSystemInfo *sys_info) { + const char *proc_cpu_path = "/proc/cpuinfo"; + char line[128]; + char vendor_id[13]; + const char vendor_id_name[] = "vendor_id"; + const size_t vendor_id_name_length = sizeof(vendor_id_name) - 1; + + struct CpuInfoEntry { + const char *info_name; + int value; + } cpu_info_table[] = { + { "processor", -1 }, + { "model", 0 }, + { "stepping", 0 }, + { "cpuid level", 0 }, + { NULL, -1 }, + }; + + memset(vendor_id, 0, sizeof(vendor_id)); + + FILE *fp = fopen(proc_cpu_path, "r"); + if (fp != NULL) { + while (fgets(line, sizeof(line), fp)) { + CpuInfoEntry *entry = &cpu_info_table[0]; + while (entry->info_name != NULL) { + if (!strncmp(line, entry->info_name, strlen(entry->info_name))) { + char *value = strchr(line, ':'); + value++; + if (value != NULL) + sscanf(value, " %d", &(entry->value)); + } + entry++; + } + + // special case for vendor_id + if (!strncmp(line, vendor_id_name, vendor_id_name_length)) { + char *value = strchr(line, ':'); + if (value == NULL) + continue; + + value++; + while (*value && isspace(*value)) + value++; + if (*value) { + size_t length = strlen(value); + // we don't want the trailing newline + if (value[length - 1] == '\n') + length--; + // ensure we have space for the value + if (length < sizeof(vendor_id)) + strncpy(vendor_id, value, length); + } + } + } + fclose(fp); + } + + // /proc/cpuinfo contains cpu id, change it into number by adding one. + cpu_info_table[0].value++; + + sys_info->number_of_processors = cpu_info_table[0].value; + sys_info->processor_level = cpu_info_table[3].value; + sys_info->processor_revision = cpu_info_table[1].value << 8 | + cpu_info_table[2].value; + + sys_info->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN; + struct utsname uts; + if (uname(&uts) == 0) { + // Match i*86 and x86* as X86 architecture. + if ((strstr(uts.machine, "x86") == uts.machine) || + (strlen(uts.machine) == 4 && + uts.machine[0] == 'i' && + uts.machine[2] == '8' && + uts.machine[3] == '6')) { + sys_info->processor_architecture = MD_CPU_ARCHITECTURE_X86; + if (vendor_id[0] != '\0') + memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id, + sizeof(sys_info->cpu.x86_cpu_info.vendor_id)); + } + } + return true; +} + +bool WriteOSInformation(MinidumpFileWriter *minidump_writer, + MDRawSystemInfo *sys_info) { + sys_info->platform_id = MD_OS_LINUX; + + struct utsname uts; + if (uname(&uts) == 0) { + char os_version[512]; + size_t space_left = sizeof(os_version); + memset(os_version, 0, space_left); + const char *os_info_table[] = { + uts.sysname, + uts.release, + uts.version, + uts.machine, + "GNU/Linux", + NULL + }; + for (const char **cur_os_info = os_info_table; + *cur_os_info != NULL; + cur_os_info++) { + if (cur_os_info != os_info_table && space_left > 1) { + strcat(os_version, " "); + space_left--; + } + if (space_left > strlen(*cur_os_info)) { + strcat(os_version, *cur_os_info); + space_left -= strlen(*cur_os_info); + } else { + break; + } + } + + MDLocationDescriptor location; + if (!minidump_writer->WriteString(os_version, 0, &location)) + return false; + sys_info->csd_version_rva = location.rva; + } + return true; +} + +// Callback context for get writting thread information. +struct ThreadInfoCallbackCtx { + MinidumpFileWriter *minidump_writer; + const WriterArgument *writer_args; + TypedMDRVA *list; + int thread_index; +}; + +// Callback run for writing threads information in the process. +bool ThreadInfomationCallback(const ThreadInfo &thread_info, + void *context) { + ThreadInfoCallbackCtx *callback_context = + static_cast(context); + bool success = true; + MDRawThread thread; + memset(&thread, 0, sizeof(MDRawThread)); + if (thread_info.pid != callback_context->writer_args->crashed_pid || + callback_context->writer_args->sig_ctx == NULL) { + success = WriteThreadStream(callback_context->minidump_writer, + callback_context->writer_args->thread_lister, + thread_info, &thread); + } else { + success = WriteCrashedThreadStream(callback_context->minidump_writer, + callback_context->writer_args, + thread_info, &thread); + } + if (success) { + callback_context->list->CopyIndexAfterObject( + callback_context->thread_index++, + &thread, sizeof(MDRawThread)); + } + return success; +} + +// Stream writers +bool WriteThreadListStream(MinidumpFileWriter *minidump_writer, + const WriterArgument *writer_args, + MDRawDirectory *dir) { + // Get the thread information. + const LinuxThread *thread_lister = writer_args->thread_lister; + int thread_count = thread_lister->GetThreadCount(); + if (thread_count < 0) + return false; + TypedMDRVA list(minidump_writer); + if (!list.AllocateObjectAndArray(thread_count, sizeof(MDRawThread))) + return false; + dir->stream_type = MD_THREAD_LIST_STREAM; + dir->location = list.location(); + list.get()->number_of_threads = thread_count; + + ThreadInfoCallbackCtx context; + context.minidump_writer = minidump_writer; + context.writer_args = writer_args; + context.list = &list; + context.thread_index = 0; + CallbackParam callback_param(ThreadInfomationCallback, + &context); + int written = thread_lister->ListThreads(&callback_param); + return written == thread_count; +} + +bool WriteCVRecord(MinidumpFileWriter *minidump_writer, + MDRawModule *module, + const char *module_path) { + TypedMDRVA cv(minidump_writer); + + // Only return the last path component of the full module path + const char *module_name = strrchr(module_path, '/'); + // Increment past the slash + if (module_name) + ++module_name; + else + module_name = ""; + + size_t module_name_length = strlen(module_name); + if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(u_int8_t))) + return false; + if (!cv.CopyIndexAfterObject(0, const_cast(module_name), + module_name_length)) + return false; + + module->cv_record = cv.location(); + MDCVInfoPDB70 *cv_ptr = cv.get(); + memset(cv_ptr, 0, sizeof(MDCVInfoPDB70)); + cv_ptr->cv_signature = MD_CVINFOPDB70_SIGNATURE; + cv_ptr->age = 0; + + // Get the module identifier + FileID file_id(module_path); + unsigned char identifier[16]; + + if (file_id.ElfFileIdentifier(identifier)) { + cv_ptr->signature.data1 = (uint32_t)identifier[0] << 24 | + (uint32_t)identifier[1] << 16 | (uint32_t)identifier[2] << 8 | + (uint32_t)identifier[3]; + cv_ptr->signature.data2 = (uint32_t)identifier[4] << 8 | identifier[5]; + cv_ptr->signature.data3 = (uint32_t)identifier[6] << 8 | identifier[7]; + cv_ptr->signature.data4[0] = identifier[8]; + cv_ptr->signature.data4[1] = identifier[9]; + cv_ptr->signature.data4[2] = identifier[10]; + cv_ptr->signature.data4[3] = identifier[11]; + cv_ptr->signature.data4[4] = identifier[12]; + cv_ptr->signature.data4[5] = identifier[13]; + cv_ptr->signature.data4[6] = identifier[14]; + cv_ptr->signature.data4[7] = identifier[15]; + } + return true; +} + +struct ModuleInfoCallbackCtx { + MinidumpFileWriter *minidump_writer; + const WriterArgument *writer_args; + TypedMDRVA *list; + int module_index; +}; + +bool ModuleInfoCallback(const ModuleInfo &module_info, + void *context) { + ModuleInfoCallbackCtx *callback_context = + static_cast(context); + // Skip those modules without name, or those that are not modules. + if (strlen(module_info.name) == 0 || + !strchr(module_info.name, '/')) + return true; + + MDRawModule module; + memset(&module, 0, sizeof(module)); + MDLocationDescriptor loc; + if (!callback_context->minidump_writer->WriteString(module_info.name, 0, + &loc)) + return false; + module.base_of_image = (u_int64_t)module_info.start_addr; + module.size_of_image = module_info.size; + module.module_name_rva = loc.rva; + + if (!WriteCVRecord(callback_context->minidump_writer, &module, + module_info.name)) + return false; + callback_context->list->CopyIndexAfterObject( + callback_context->module_index++, &module, MD_MODULE_SIZE); + return true; +} + +bool WriteModuleListStream(MinidumpFileWriter *minidump_writer, + const WriterArgument *writer_args, + MDRawDirectory *dir) { + TypedMDRVA list(minidump_writer); + int module_count = writer_args->thread_lister->GetModuleCount(); + if (module_count <= 0 || + !list.AllocateObjectAndArray(module_count, MD_MODULE_SIZE)) + return false; + dir->stream_type = MD_MODULE_LIST_STREAM; + dir->location = list.location(); + list.get()->number_of_modules = module_count; + ModuleInfoCallbackCtx context; + context.minidump_writer = minidump_writer; + context.writer_args = writer_args; + context.list = &list; + context.module_index = 0; + CallbackParam callback(ModuleInfoCallback, &context); + return writer_args->thread_lister->ListModules(&callback) == module_count; +} + +bool WriteSystemInfoStream(MinidumpFileWriter *minidump_writer, + const WriterArgument *writer_args, + MDRawDirectory *dir) { + TypedMDRVA sys_info(minidump_writer); + if (!sys_info.Allocate()) + return false; + dir->stream_type = MD_SYSTEM_INFO_STREAM; + dir->location = sys_info.location(); + + return WriteCPUInformation(sys_info.get()) && + WriteOSInformation(minidump_writer, sys_info.get()); +} + +bool WriteExceptionStream(MinidumpFileWriter *minidump_writer, + const WriterArgument *writer_args, + MDRawDirectory *dir) { + // This happenes when this is not a crash, but a requested dump. + if (writer_args->sig_ctx == NULL) + return false; + + TypedMDRVA exception(minidump_writer); + if (!exception.Allocate()) + return false; + + dir->stream_type = MD_EXCEPTION_STREAM; + dir->location = exception.location(); + exception.get()->thread_id = writer_args->crashed_pid; + exception.get()->exception_record.exception_code = writer_args->signo; + exception.get()->exception_record.exception_flags = 0; + if (writer_args->sig_ctx != NULL) { + exception.get()->exception_record.exception_address = + writer_args->sig_ctx->eip; + } else { + return true; + } + + // Write context of the exception. + TypedMDRVA context(minidump_writer); + if (!context.Allocate()) + return false; + exception.get()->thread_context = context.location(); + memset(context.get(), 0, sizeof(MDRawContextX86)); + return WriteContext(context.get(), writer_args->sig_ctx, NULL); +} + +bool WriteMiscInfoStream(MinidumpFileWriter *minidump_writer, + const WriterArgument *writer_args, + MDRawDirectory *dir) { + TypedMDRVA info(minidump_writer); + if (!info.Allocate()) + return false; + + dir->stream_type = MD_MISC_INFO_STREAM; + dir->location = info.location(); + info.get()->size_of_info = sizeof(MDRawMiscInfo); + info.get()->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID; + info.get()->process_id = writer_args->requester_pid; + + return true; +} + +bool WriteBreakpadInfoStream(MinidumpFileWriter *minidump_writer, + const WriterArgument *writer_args, + MDRawDirectory *dir) { + TypedMDRVA info(minidump_writer); + if (!info.Allocate()) + return false; + + dir->stream_type = MD_BREAKPAD_INFO_STREAM; + dir->location = info.location(); + + info.get()->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | + MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; + info.get()->dump_thread_id = getpid(); + info.get()->requesting_thread_id = writer_args->requester_pid; + return true; +} + +// Prototype of writer functions. +typedef bool (*WriteStringFN)(MinidumpFileWriter *, + const WriterArgument *, + MDRawDirectory *); + +// Function table to writer a full minidump. +WriteStringFN writers[] = { + WriteThreadListStream, + WriteModuleListStream, + WriteSystemInfoStream, + WriteExceptionStream, + WriteMiscInfoStream, + WriteBreakpadInfoStream, +}; + +// Will call each writer function in the writers table. +// It runs in a different process from the crashing process, but sharing +// the same address space. This enables it to use ptrace functions. +int Write(void *argument) { + WriterArgument *writer_args = + static_cast(argument); + + if (!writer_args->thread_lister->SuspendAllThreads()) + return -1; + + if (writer_args->sighandler_ebp != 0 && + writer_args->thread_lister->FindSigContext(writer_args->sighandler_ebp, + &writer_args->sig_ctx)) { + writer_args->crashed_stack_bottom = + writer_args->thread_lister->GetThreadStackBottom( + writer_args->sig_ctx->ebp); + int crashed_pid = FindCrashingThread(writer_args->crashed_stack_bottom, + writer_args->requester_pid, + writer_args->thread_lister); + if (crashed_pid > 0) + writer_args->crashed_pid = crashed_pid; + } + + + MinidumpFileWriter *minidump_writer = writer_args->minidump_writer; + TypedMDRVA header(minidump_writer); + TypedMDRVA dir(minidump_writer); + if (!header.Allocate()) + return 0; + + int writer_count = sizeof(writers) / sizeof(writers[0]); + // Need directory space for all writers. + if (!dir.AllocateArray(writer_count)) + return 0; + header.get()->signature = MD_HEADER_SIGNATURE; + header.get()->version = MD_HEADER_VERSION; + header.get()->time_date_stamp = time(NULL); + header.get()->stream_count = writer_count; + header.get()->stream_directory_rva = dir.position(); + + int dir_index = 0; + MDRawDirectory local_dir; + for (int i = 0; i < writer_count; ++i) { + if (writers[i](minidump_writer, writer_args, &local_dir)) + dir.CopyIndex(dir_index++, &local_dir); + } + + writer_args->thread_lister->ResumeAllThreads(); + return 0; +} + +} // namespace + +namespace google_breakpad { + +MinidumpGenerator::MinidumpGenerator() { + AllocateStack(); +} + +MinidumpGenerator::~MinidumpGenerator() { +} + +void MinidumpGenerator::AllocateStack() { + stack_.reset(new char[kStackSize]); +} + +bool MinidumpGenerator::WriteMinidumpToFile(const char *file_pathname, + int signo, + uintptr_t sighandler_ebp, + struct sigcontext **sig_ctx) const { + assert(file_pathname != NULL); + assert(stack_ != NULL); + + if (stack_ == NULL || file_pathname == NULL) + return false; + + MinidumpFileWriter minidump_writer; + if (minidump_writer.Open(file_pathname)) { + WriterArgument argument; + memset(&argument, 0, sizeof(argument)); + LinuxThread thread_lister(getpid()); + argument.thread_lister = &thread_lister; + argument.minidump_writer = &minidump_writer; + argument.requester_pid = getpid(); + argument.crashed_pid = getpid(); + argument.signo = signo; + argument.sighandler_ebp = sighandler_ebp; + argument.sig_ctx = NULL; + + int cloned_pid = clone(Write, stack_.get() + kStackSize, + CLONE_VM | CLONE_FILES | CLONE_FS | CLONE_UNTRACED, + (void*)&argument); + waitpid(cloned_pid, NULL, __WALL); + if (sig_ctx != NULL) + *sig_ctx = argument.sig_ctx; + return true; + } + + return false; +} + +} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.h b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.h similarity index 61% rename from toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.h rename to toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.h index 579f68cdae7..7c0511f5649 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.h +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.h @@ -1,6 +1,8 @@ -// Copyright (c) 2009, Google Inc. +// Copyright (c) 2006, Google Inc. // All rights reserved. // +// Author: Li Liu +// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -27,27 +29,45 @@ // (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_MINIDUMP_WRITER_H_ -#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ +#ifndef CLIENT_LINUX_HANDLER_MINIDUMP_GENERATOR_H__ +#define CLIENT_LINUX_HANDLER_MINIDUMP_GENERATOR_H__ #include -#include + +#include "google_breakpad/common/breakpad_types.h" +#include "processor/scoped_ptr.h" + +struct sigcontext; namespace google_breakpad { -// Write a minidump to the filesystem. This function does not malloc nor use -// libc functions which may. Thus, it can be used in contexts where the state -// of the heap may be corrupt. -// filename: the filename to write to. This is opened O_EXCL and fails if -// open fails. -// crashing_process: the pid of the crashing process. This must be trusted. -// blob: a blob of data from the crashing process. See exception_handler.h -// blob_size: the length of |blob|, in bytes // -// Returns true iff successful. -bool WriteMinidump(const char* filename, pid_t crashing_process, - const void* blob, size_t blob_size); +// MinidumpGenerator +// +// Write a minidump to file based on the signo and sig_ctx. +// A minidump generator should be created before any exception happen. +// +class MinidumpGenerator { + public: + MinidumpGenerator(); + + ~MinidumpGenerator(); + + // Write minidump. + bool WriteMinidumpToFile(const char *file_pathname, + int signo, + uintptr_t sighandler_ebp, + struct sigcontext **sig_ctx) const; + private: + // Allocate memory for stack. + void AllocateStack(); + + private: + // Stack size of the writer thread. + static const int kStackSize = 1024 * 1024; + scoped_array stack_; +}; } // namespace google_breakpad -#endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ +#endif // CLIENT_LINUX_HANDLER_MINIDUMP_GENERATOR_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_test.cc similarity index 57% rename from toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc rename to toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_test.cc index 5ff336ce972..f8c4e78441b 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc +++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_test.cc @@ -1,6 +1,8 @@ -// Copyright (c) 2009, Google Inc. +// Copyright (c) 2006, Google Inc. // All rights reserved. // +// Author: Li Liu +// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -27,53 +29,58 @@ // (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 #include -#include -#include "client/linux/handler/exception_handler.h" -#include "client/linux/minidump_writer/minidump_writer.h" -#include "breakpad_googletest_includes.h" +#include +#include +#include +#include + +#include "client/linux/handler/minidump_generator.h" using namespace google_breakpad; -// This provides a wrapper around system calls which may be -// interrupted by a signal and return EINTR. See man 7 signal. -#define HANDLE_EINTR(x) ({ \ - typeof(x) __eintr_result__; \ - do { \ - __eintr_result__ = x; \ - } while (__eintr_result__ == -1 && errno == EINTR); \ - __eintr_result__;\ -}) +// Thread use this to see if it should stop working. +static bool should_exit = false; -namespace { -typedef testing::Test MinidumpWriterTest; +static void foo2(int arg) { + // Stack variable, used for debugging stack dumps. + int c = arg; + c = 0xcccccccc; + while (!should_exit) + sleep(1); } -TEST(MinidumpWriterTest, Setup) { - int fds[2]; - ASSERT_NE(-1, pipe(fds)); +static void foo(int arg) { + // Stack variable, used for debugging stack dumps. + int b = arg; + b = 0xbbbbbbbb; + foo2(b); +} - const pid_t child = fork(); - if (child == 0) { - close(fds[1]); - char b; - HANDLE_EINTR(read(fds[0], &b, sizeof(b))); - close(fds[0]); - syscall(__NR_exit); +static void *thread_main(void *) { + // Stack variable, used for debugging stack dumps. + int a = 0xaaaaaaaa; + foo(a); + return NULL; +} + +static void CreateThread(int num) { + pthread_t h; + for (int i = 0; i < num; ++i) { + pthread_create(&h, NULL, thread_main, NULL); + pthread_detach(h); } - close(fds[0]); - - ExceptionHandler::CrashContext context; - memset(&context, 0, sizeof(context)); - - char templ[] = "/tmp/minidump-writer-unittest-XXXXXX"; - mktemp(templ); - ASSERT_TRUE(WriteMinidump(templ, child, &context, sizeof(context))); - struct stat st; - ASSERT_EQ(stat(templ, &st), 0); - ASSERT_GT(st.st_size, 0u); - unlink(templ); - - close(fds[1]); +} + +int main(int argc, char *argv[]) { + CreateThread(10); + google_breakpad::MinidumpGenerator mg; + if (mg.WriteMinidumpToFile("minidump_test.out", -1, 0, NULL)) + printf("Succeeded written minidump\n"); + else + printf("Failed to write minidump\n"); + should_exit = true; + return 0; } diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/Makefile.in b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/Makefile.in deleted file mode 100644 index 51a7cfdfd29..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/Makefile.in +++ /dev/null @@ -1,60 +0,0 @@ -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Breakpad integration -# -# The Initial Developer of the Original Code is -# The Mozilla Foundation. -# Portions created by the Initial Developer are Copyright (C) 2009 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Ted Mielczarek -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../../../../../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -MODULE = writer -LIBRARY_NAME = minidump_writer_s -XPI_NAME = crashreporter - -LOCAL_INCLUDES = -I$(srcdir)/../../.. - -CPPSRCS = \ - linux_dumper.cc \ - minidump_writer.cc \ - $(NULL) - -# need static lib -FORCE_STATIC_LIB = 1 -FORCE_USE_PIC = 1 - -include $(topsrcdir)/config/rules.mk diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader.h b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader.h deleted file mode 100644 index 4698b7e5b91..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader.h +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) 2009, 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_DIRECTORY_READER_H_ -#define CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ - -#include -#include -#include -#include -#include -#include - -#include "common/linux/linux_syscall_support.h" - -namespace google_breakpad { - -// A class for enumerating a directory without using diropen/readdir or other -// functions which may allocate memory. -class DirectoryReader { - public: - DirectoryReader(int fd) - : fd_(fd), - buf_used_(0) { - } - - // Return the next entry from the directory - // name: (output) the NUL terminated entry name - // - // Returns true iff successful (false on EOF). - // - // After calling this, one must call |PopEntry| otherwise you'll get the same - // entry over and over. - bool GetNextEntry(const char** name) { - struct kernel_dirent* const dent = - reinterpret_cast(buf_); - - if (buf_used_ == 0) { - // need to read more entries. - const int n = sys_getdents(fd_, dent, sizeof(buf_)); - if (n < 0) { - return false; - } else if (n == 0) { - hit_eof_ = true; - } else { - buf_used_ += n; - } - } - - if (buf_used_ == 0 && hit_eof_) - return false; - - assert(buf_used_ > 0); - - *name = dent->d_name; - return true; - } - - void PopEntry() { - if (!buf_used_) - return; - - const struct kernel_dirent* const dent = - reinterpret_cast(buf_); - - buf_used_ -= dent->d_reclen; - memmove(buf_, buf_ + dent->d_reclen, buf_used_); - } - - private: - const int fd_; - bool hit_eof_; - unsigned buf_used_; - uint8_t buf_[sizeof(struct kernel_dirent) + NAME_MAX + 1]; -}; - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader_unittest.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader_unittest.cc deleted file mode 100644 index 3034e619c48..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader_unittest.cc +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) 2009, 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 -#include - -#include -#include -#include - -#include "client/linux/minidump_writer/directory_reader.h" -#include "breakpad_googletest_includes.h" - -using namespace google_breakpad; - -namespace { -typedef testing::Test DirectoryReaderTest; -} - -TEST(DirectoryReaderTest, CompareResults) { - std::set dent_set; - - DIR *const dir = opendir("/proc/self"); - ASSERT_TRUE(dir != NULL); - - struct dirent* dent; - while ((dent = readdir(dir))) - dent_set.insert(dent->d_name); - - closedir(dir); - - const int fd = open("/proc/self", O_DIRECTORY | O_RDONLY); - ASSERT_GE(fd, 0); - - DirectoryReader dir_reader(fd); - unsigned seen = 0; - - const char* name; - while (dir_reader.GetNextEntry(&name)) { - ASSERT_TRUE(dent_set.find(name) != dent_set.end()); - seen++; - dir_reader.PopEntry(); - } - - ASSERT_TRUE(dent_set.find("status") != dent_set.end()); - ASSERT_TRUE(dent_set.find("stat") != dent_set.end()); - ASSERT_TRUE(dent_set.find("cmdline") != dent_set.end()); - - ASSERT_EQ(dent_set.size(), seen); - close(fd); -} diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader.h b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader.h deleted file mode 100644 index 5c0a1154b37..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader.h +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (c) 2009, 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_LINE_READER_H_ -#define CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ - -#include -#include -#include - -#include "common/linux/linux_syscall_support.h" - -namespace google_breakpad { - -// A class for reading a file, line by line, without using fopen/fgets or other -// functions which may allocate memory. -class LineReader { - public: - LineReader(int fd) - : fd_(fd), - hit_eof_(false), - buf_used_(0) { - } - - // The maximum length of a line. - static const size_t kMaxLineLen = 512; - - // Return the next line from the file. - // line: (output) a pointer to the start of the line. The line is NUL - // terminated. - // len: (output) the length of the line (not inc the NUL byte) - // - // Returns true iff successful (false on EOF). - // - // One must call |PopLine| after this function, otherwise you'll continue to - // get the same line over and over. - bool GetNextLine(const char **line, unsigned *len) { - for (;;) { - if (buf_used_ == 0 && hit_eof_) - return false; - - for (unsigned i = 0; i < buf_used_; ++i) { - if (buf_[i] == '\n' || buf_[i] == 0) { - buf_[i] = 0; - *len = i; - *line = buf_; - return true; - } - } - - if (buf_used_ == sizeof(buf_)) { - // we scanned the whole buffer and didn't find an end-of-line marker. - // This line is too long to process. - return false; - } - - // We didn't find any end-of-line terminators in the buffer. However, if - // this is the last line in the file it might not have one: - if (hit_eof_) { - assert(buf_used_); - // There's room for the NUL because of the buf_used_ == sizeof(buf_) - // check above. - buf_[buf_used_] = 0; - *len = buf_used_; - buf_used_ += 1; // since we appended the NUL. - *line = buf_; - return true; - } - - // Otherwise, we should pull in more data from the file - const ssize_t n = sys_read(fd_, buf_ + buf_used_, - sizeof(buf_) - buf_used_); - if (n < 0) { - return false; - } else if (n == 0) { - hit_eof_ = true; - } else { - buf_used_ += n; - } - - // At this point, we have either set the hit_eof_ flag, or we have more - // data to process... - } - } - - void PopLine(unsigned len) { - // len doesn't include the NUL byte at the end. - - assert(buf_used_ >= len + 1); - buf_used_ -= len + 1; - memmove(buf_, buf_ + len + 1, buf_used_); - } - - private: - const int fd_; - - bool hit_eof_; - unsigned buf_used_; - char buf_[kMaxLineLen]; -}; - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader_unittest.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader_unittest.cc deleted file mode 100644 index 222a098eee1..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader_unittest.cc +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (c) 2009, 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 -#include -#include - -#include "client/linux/minidump_writer/line_reader.h" -#include "breakpad_googletest_includes.h" - -using namespace google_breakpad; - -static int TemporaryFile() { - static const char templ[] = "/tmp/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; -} - -TEST(LineReaderTest, EmptyFile) { - const int fd = TemporaryFile(); - LineReader reader(fd); - - const char *line; - unsigned len; - ASSERT_FALSE(reader.GetNextLine(&line, &len)); - - close(fd); -} - -TEST(LineReaderTest, OneLineTerminated) { - const int fd = TemporaryFile(); - write(fd, "a\n", 2); - lseek(fd, 0, SEEK_SET); - LineReader reader(fd); - - const char *line; - unsigned len; - ASSERT_TRUE(reader.GetNextLine(&line, &len)); - ASSERT_EQ(len, 1); - ASSERT_EQ(line[0], 'a'); - ASSERT_EQ(line[1], 0); - reader.PopLine(len); - - ASSERT_FALSE(reader.GetNextLine(&line, &len)); - - close(fd); -} - -TEST(LineReaderTest, OneLine) { - const int fd = TemporaryFile(); - write(fd, "a", 1); - lseek(fd, 0, SEEK_SET); - LineReader reader(fd); - - const char *line; - unsigned len; - ASSERT_TRUE(reader.GetNextLine(&line, &len)); - ASSERT_EQ(len, 1); - ASSERT_EQ(line[0], 'a'); - ASSERT_EQ(line[1], 0); - reader.PopLine(len); - - ASSERT_FALSE(reader.GetNextLine(&line, &len)); - - close(fd); -} - -TEST(LineReaderTest, TwoLinesTerminated) { - const int fd = TemporaryFile(); - write(fd, "a\nb\n", 4); - lseek(fd, 0, SEEK_SET); - LineReader reader(fd); - - const char *line; - unsigned len; - ASSERT_TRUE(reader.GetNextLine(&line, &len)); - ASSERT_EQ(len, 1); - ASSERT_EQ(line[0], 'a'); - ASSERT_EQ(line[1], 0); - reader.PopLine(len); - - ASSERT_TRUE(reader.GetNextLine(&line, &len)); - ASSERT_EQ(len, 1); - ASSERT_EQ(line[0], 'b'); - ASSERT_EQ(line[1], 0); - reader.PopLine(len); - - ASSERT_FALSE(reader.GetNextLine(&line, &len)); - - close(fd); -} - -TEST(LineReaderTest, TwoLines) { - const int fd = TemporaryFile(); - write(fd, "a\nb", 3); - lseek(fd, 0, SEEK_SET); - LineReader reader(fd); - - const char *line; - unsigned len; - ASSERT_TRUE(reader.GetNextLine(&line, &len)); - ASSERT_EQ(len, 1); - ASSERT_EQ(line[0], 'a'); - ASSERT_EQ(line[1], 0); - reader.PopLine(len); - - ASSERT_TRUE(reader.GetNextLine(&line, &len)); - ASSERT_EQ(len, 1); - ASSERT_EQ(line[0], 'b'); - ASSERT_EQ(line[1], 0); - reader.PopLine(len); - - ASSERT_FALSE(reader.GetNextLine(&line, &len)); - - close(fd); -} - -TEST(LineReaderTest, MaxLength) { - const int fd = TemporaryFile(); - char l[LineReader::kMaxLineLen - 1]; - memset(l, 'a', sizeof(l)); - write(fd, l, sizeof(l)); - lseek(fd, 0, SEEK_SET); - LineReader reader(fd); - - const char *line; - unsigned len; - ASSERT_TRUE(reader.GetNextLine(&line, &len)); - ASSERT_EQ(len, sizeof(l)); - ASSERT_TRUE(memcmp(l, line, sizeof(l)) == 0); - ASSERT_EQ(line[len], 0); - - close(fd); -} - -TEST(LineReaderTest, TooLong) { - const int fd = TemporaryFile(); - char l[LineReader::kMaxLineLen]; - memset(l, 'a', sizeof(l)); - write(fd, l, sizeof(l)); - lseek(fd, 0, SEEK_SET); - LineReader reader(fd); - - const char *line; - unsigned len; - ASSERT_FALSE(reader.GetNextLine(&line, &len)); - - close(fd); -} diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.cc deleted file mode 100644 index 86ebb866eee..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.cc +++ /dev/null @@ -1,421 +0,0 @@ -// Copyright (c) 2009, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// This code deals with the mechanics of getting information about a crashed -// process. Since this code may run in a compromised address space, the same -// rules apply as detailed at the top of minidump_writer.h: no libc calls and -// use the alternative allocator. - -#include "client/linux/minidump_writer/linux_dumper.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "client/linux/minidump_writer/directory_reader.h" -#include "client/linux/minidump_writer/line_reader.h" -#include "common/linux/linux_libc_support.h" -#include "common/linux/linux_syscall_support.h" - -// Suspend a thread by attaching to it. -static bool SuspendThread(pid_t pid) { - // This may fail if the thread has just died or debugged. - errno = 0; - if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 && - errno != 0) { - return false; - } - while (sys_waitpid(pid, NULL, __WALL) < 0) { - if (errno != EINTR) { - sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); - return false; - } - } - return true; -} - -// Resume a thread by detaching from it. -static bool ResumeThread(pid_t pid) { - return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0; -} - -namespace google_breakpad { - -LinuxDumper::LinuxDumper(int pid) - : pid_(pid), - threads_suspened_(false), - threads_(&allocator_, 8), - mappings_(&allocator_) { -} - -bool LinuxDumper::Init() { - return EnumerateThreads(&threads_) && - EnumerateMappings(&mappings_); -} - -bool LinuxDumper::ThreadsSuspend() { - if (threads_suspened_) - return true; - bool good = true; - for (size_t i = 0; i < threads_.size(); ++i) - good &= SuspendThread(threads_[i]); - threads_suspened_ = true; - return good; -} - -bool LinuxDumper::ThreadsResume() { - if (!threads_suspened_) - return false; - bool good = true; - for (size_t i = 0; i < threads_.size(); ++i) - good &= ResumeThread(threads_[i]); - threads_suspened_ = false; - return good; -} - -void -LinuxDumper::BuildProcPath(char* path, pid_t pid, const char* node) const { - assert(path); - if (!path) { - return; - } - - path[0] = '\0'; - - const unsigned pid_len = my_int_len(pid); - - assert(node); - if (!node) { - return; - } - - size_t node_len = my_strlen(node); - assert(node_len < NAME_MAX); - if (node_len >= NAME_MAX) { - return; - } - - assert(node_len > 0); - if (node_len == 0) { - return; - } - - assert(pid > 0); - if (pid <= 0) { - return; - } - - const size_t total_length = 6 + pid_len + 1 + node_len; - - assert(total_length < NAME_MAX); - if (total_length >= NAME_MAX) { - return; - } - - memcpy(path, "/proc/", 6); - my_itos(path + 6, pid, pid_len); - memcpy(path + 6 + pid_len, "/", 1); - memcpy(path + 6 + pid_len + 1, node, node_len); - memcpy(path + total_length, "\0", 1); -} - -void* -LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const { - char auxv_path[80]; - BuildProcPath(auxv_path, pid, "auxv"); - - // If BuildProcPath errors out due to invalid input, we'll handle it when - // we try to sys_open the file. - - // Find the AT_SYSINFO_EHDR entry for linux-gate.so - // See http://www.trilithium.com/johan/2005/08/linux-gate/ for more - // information. - int fd = sys_open(auxv_path, O_RDONLY, 0); - if (fd < 0) { - return NULL; - } - - elf_aux_entry one_aux_entry; - while (sys_read(fd, - &one_aux_entry, - sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) && - one_aux_entry.a_type != AT_NULL) { - if (one_aux_entry.a_type == AT_SYSINFO_EHDR) { - close(fd); - return reinterpret_cast(one_aux_entry.a_un.a_val); - } - } - close(fd); - return NULL; -} - -bool -LinuxDumper::EnumerateMappings(wasteful_vector* result) const { - char maps_path[80]; - BuildProcPath(maps_path, pid_, "maps"); - - // linux_gate_loc is the beginning of the kernel's mapping of - // linux-gate.so in the process. It doesn't actually show up in the - // maps list as a filename, so we use the aux vector to find it's - // load location and special case it's entry when creating the list - // of mappings. - const void* linux_gate_loc; - linux_gate_loc = FindBeginningOfLinuxGateSharedLibrary(pid_); - - const int fd = sys_open(maps_path, O_RDONLY, 0); - if (fd < 0) - return false; - LineReader* const line_reader = new(allocator_) LineReader(fd); - - const char* line; - unsigned line_len; - while (line_reader->GetNextLine(&line, &line_len)) { - uintptr_t start_addr, end_addr, offset; - - const char* i1 = my_read_hex_ptr(&start_addr, line); - if (*i1 == '-') { - const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1); - if (*i2 == ' ') { - const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */); - if (*i3 == ' ') { - MappingInfo* const module = new(allocator_) MappingInfo; - memset(module, 0, sizeof(MappingInfo)); - module->start_addr = start_addr; - module->size = end_addr - start_addr; - module->offset = offset; - const char* name = NULL; - // Only copy name if the name is a valid path name, or if - // we've found the VDSO image - if ((name = my_strchr(line, '/')) != NULL) { - const unsigned l = my_strlen(name); - if (l < sizeof(module->name)) - memcpy(module->name, name, l); - } else if (linux_gate_loc && - reinterpret_cast(module->start_addr) == - linux_gate_loc) { - memcpy(module->name, - kLinuxGateLibraryName, - my_strlen(kLinuxGateLibraryName)); - module->offset = 0; - } - result->push_back(module); - } - } - } - line_reader->PopLine(line_len); - } - - sys_close(fd); - - return result->size() > 0; -} - -// Parse /proc/$pid/task to list all the threads of the process identified by -// pid. -bool LinuxDumper::EnumerateThreads(wasteful_vector* result) const { - char task_path[80]; - BuildProcPath(task_path, pid_, "task"); - - const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0); - if (fd < 0) - return false; - DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd); - - // The directory may contain duplicate entries which we filter by assuming - // that they are consecutive. - int last_tid = -1; - const char* dent_name; - while (dir_reader->GetNextEntry(&dent_name)) { - if (my_strcmp(dent_name, ".") && - my_strcmp(dent_name, "..")) { - int tid = 0; - if (my_strtoui(&tid, dent_name) && - last_tid != tid) { - last_tid = tid; - result->push_back(tid); - } - } - dir_reader->PopEntry(); - } - - sys_close(fd); - return true; -} - -// Read thread info from /proc/$pid/status. -// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailible, -// these members are set to -1. Returns true iff all three members are -// availible. -bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) { - assert(info != NULL); - char status_path[80]; - BuildProcPath(status_path, tid, "status"); - - const int fd = open(status_path, O_RDONLY); - if (fd < 0) - return false; - - LineReader* const line_reader = new(allocator_) LineReader(fd); - const char* line; - unsigned line_len; - - info->ppid = info->tgid = -1; - - while (line_reader->GetNextLine(&line, &line_len)) { - if (my_strncmp("Tgid:\t", line, 6) == 0) { - my_strtoui(&info->tgid, line + 6); - } else if (my_strncmp("PPid:\t", line, 6) == 0) { - my_strtoui(&info->ppid, line + 6); - } - - line_reader->PopLine(line_len); - } - - if (info->ppid == -1 || info->tgid == -1) - return false; - - if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1 || - sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) { - return false; - } - -#if defined(__i386) - if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) - return false; -#endif - -#if defined(__i386) || defined(__x86_64) - for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) { - if (sys_ptrace( - PTRACE_PEEKUSER, tid, - reinterpret_cast (offsetof(struct user, - u_debugreg[0]) + i * - sizeof(debugreg_t)), - &info->dregs[i]) == -1) { - return false; - } - } -#endif - - const uint8_t* stack_pointer; -#if defined(__i386) - memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); -#elif defined(__x86_64) - memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); -#else -#error "This code hasn't been ported to your platform yet." -#endif - - if (!GetStackInfo(&info->stack, &info->stack_len, - (uintptr_t) stack_pointer)) - return false; - - return true; -} - -// Get information about the stack, given the stack pointer. We don't try to -// walk the stack since we might not have all the information needed to do -// unwind. So we just grab, up to, 32k of stack. -bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len, - uintptr_t int_stack_pointer) { -#if defined(__i386) || defined(__x86_64) - static const bool stack_grows_down = true; - static const uintptr_t page_size = 4096; -#else -#error "This code has not been ported to your platform yet." -#endif - // Move the stack pointer to the bottom of the page that it's in. - uint8_t* const stack_pointer = - reinterpret_cast(int_stack_pointer & ~(page_size - 1)); - - // The number of bytes of stack which we try to capture. - static unsigned kStackToCapture = 32 * 1024; - - const MappingInfo* mapping = FindMapping(stack_pointer); - if (!mapping) - return false; - if (stack_grows_down) { - const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr; - const ptrdiff_t distance_to_end = - static_cast(mapping->size) - offset; - *stack_len = distance_to_end > kStackToCapture ? - kStackToCapture : distance_to_end; - *stack = stack_pointer; - } else { - const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr; - *stack_len = offset > kStackToCapture ? kStackToCapture : offset; - *stack = stack_pointer - *stack_len; - } - - return true; -} - -// static -void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src, - size_t length) { - unsigned long tmp; - size_t done = 0; - static const size_t word_size = sizeof(tmp); - uint8_t* const local = (uint8_t*) dest; - uint8_t* const remote = (uint8_t*) src; - - while (done < length) { - const size_t l = length - done > word_size ? word_size : length - done; - if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) - tmp = 0; - memcpy(local + done, &tmp, l); - done += l; - } -} - -// Find the mapping which the given memory address falls in. -const MappingInfo* LinuxDumper::FindMapping(const void* address) const { - const uintptr_t addr = (uintptr_t) address; - - for (size_t i = 0; i < mappings_.size(); ++i) { - const uintptr_t start = static_cast(mappings_[i]->start_addr); - if (addr >= start && addr - start < mappings_[i]->size) - return mappings_[i]; - } - - return NULL; -} - -} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.h b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.h deleted file mode 100644 index d8e5e783e2d..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.h +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (c) 2009, 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_LINUX_DUMPER_H_ -#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_ - -#include -#include -#include -#include -#include - -#include "common/linux/memory.h" - -namespace google_breakpad { - -typedef typeof(((struct user*) 0)->u_debugreg[0]) debugreg_t; - -// Typedef for our parsing of the auxv variables in /proc/pid/auxv. -#if defined(__i386) -typedef Elf32_auxv_t elf_aux_entry; -#elif defined(__x86_64__) -typedef Elf64_auxv_t elf_aux_entry; -#endif -// When we find the VDSO mapping in the process's address space, this -// is the name we use for it when writing it to the minidump. -// This should always be less than NAME_MAX! -const char kLinuxGateLibraryName[] = "linux-gate.so"; - -// We produce one of these structures for each thread in the crashed process. -struct ThreadInfo { - pid_t tgid; // thread group id - pid_t ppid; // parent process - - // Even on platforms where the stack grows down, the following will point to - // the smallest address in the stack. - const void* stack; // pointer to the stack area - size_t stack_len; // length of the stack to copy - - user_regs_struct regs; - user_fpregs_struct fpregs; -#if defined(__i386) - user_fpxregs_struct fpxregs; -#endif - -#if defined(__i386) || defined(__x86_64) - - static const unsigned kNumDebugRegisters = 8; - debugreg_t dregs[8]; -#endif -}; - -// One of these is produced for each mapping in the process (i.e. line in -// /proc/$x/maps). -struct MappingInfo { - uintptr_t start_addr; - size_t size; - size_t offset; // offset into the backed file. - char name[NAME_MAX]; -}; - -class LinuxDumper { - public: - explicit LinuxDumper(pid_t pid); - - // Parse the data for |threads| and |mappings|. - bool Init(); - - // Suspend/resume all threads in the given process. - bool ThreadsSuspend(); - bool ThreadsResume(); - - // Read information about the given thread. Returns true on success. One must - // have called |ThreadsSuspend| first. - bool ThreadInfoGet(pid_t tid, ThreadInfo* info); - - // These are only valid after a call to |Init|. - const wasteful_vector &threads() { return threads_; } - const wasteful_vector &mappings() { return mappings_; } - const MappingInfo* FindMapping(const void* address) const; - - // Find a block of memory to take as the stack given the top of stack pointer. - // stack: (output) the lowest address in the memory area - // stack_len: (output) the length of the memory area - // stack_top: the current top of the stack - bool GetStackInfo(const void** stack, size_t* stack_len, uintptr_t stack_top); - - PageAllocator* allocator() { return &allocator_; } - - // memcpy from a remote process. - static void CopyFromProcess(void* dest, pid_t child, const void* src, - size_t length); - - // Builds a proc path for a certain pid for a node. path is a - // character array that is overwritten, and node is the final node - // without any slashes. - void BuildProcPath(char* path, pid_t pid, const char* node) const; - - // Utility method to find the location of where the kernel has - // mapped linux-gate.so in memory(shows up in /proc/pid/maps as - // [vdso], but we can't guarantee that it's the only virtual dynamic - // shared object. Parsing the auxilary vector for AT_SYSINFO_EHDR - // is the safest way to go.) - void* FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const; - private: - bool EnumerateMappings(wasteful_vector* result) const; - bool EnumerateThreads(wasteful_vector* result) const; - - const pid_t pid_; - - mutable PageAllocator allocator_; - - bool threads_suspened_; - wasteful_vector threads_; // the ids of all the threads - wasteful_vector mappings_; // info from /proc//maps -}; - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest.cc deleted file mode 100644 index f5ed914b2d5..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest.cc +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright (c) 2009, 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 - -#include "client/linux/minidump_writer/linux_dumper.h" -#include "breakpad_googletest_includes.h" - -using namespace google_breakpad; - -namespace { -typedef testing::Test LinuxDumperTest; -} - -TEST(LinuxDumperTest, Setup) { - LinuxDumper dumper(getpid()); -} - -TEST(LinuxDumperTest, FindMappings) { - LinuxDumper dumper(getpid()); - ASSERT_TRUE(dumper.Init()); - - ASSERT_TRUE(dumper.FindMapping(reinterpret_cast(getpid))); - ASSERT_TRUE(dumper.FindMapping(reinterpret_cast(printf))); - ASSERT_FALSE(dumper.FindMapping(NULL)); -} - -TEST(LinuxDumperTest, ThreadList) { - LinuxDumper dumper(getpid()); - ASSERT_TRUE(dumper.Init()); - - ASSERT_GE(dumper.threads().size(), 1); - bool found = false; - for (size_t i = 0; i < dumper.threads().size(); ++i) { - if (dumper.threads()[i] == getpid()) { - found = true; - break; - } - } -} - -TEST(LinuxDumperTest, BuildProcPath) { - const pid_t pid = getpid(); - LinuxDumper dumper(pid); - - char maps_path[256] = "dummymappath"; - char maps_path_expected[256]; - snprintf(maps_path_expected, sizeof(maps_path_expected), - "/proc/%d/maps", pid); - dumper.BuildProcPath(maps_path, pid, "maps"); - ASSERT_STREQ(maps_path, maps_path_expected); - - // In release mode, we expect BuildProcPath to handle the invalid - // parameters correctly and fill map_path with an empty - // NULL-terminated string. -#ifdef NDEBUG - snprintf(maps_path, sizeof(maps_path), "dummymappath"); - dumper.BuildProcPath(maps_path, 0, "maps"); - EXPECT_STREQ(maps_path, ""); - - snprintf(maps_path, sizeof(maps_path), "dummymappath"); - dumper.BuildProcPath(maps_path, getpid(), ""); - EXPECT_STREQ(maps_path, ""); - - snprintf(maps_path, sizeof(maps_path), "dummymappath"); - dumper.BuildProcPath(maps_path, getpid(), NULL); - EXPECT_STREQ(maps_path, ""); -#endif -} - -TEST(LinuxDumperTest, MappingsIncludeLinuxGate) { - LinuxDumper dumper(getpid()); - ASSERT_TRUE(dumper.Init()); - - void* linux_gate_loc = dumper.FindBeginningOfLinuxGateSharedLibrary(getpid()); - if (linux_gate_loc) { - bool found_linux_gate = false; - - const wasteful_vector mappings = dumper.mappings(); - const MappingInfo* mapping; - for (unsigned i = 0; i < mappings.size(); ++i) { - mapping = mappings[i]; - if (!strcmp(mapping->name, kLinuxGateLibraryName)) { - found_linux_gate = true; - break; - } - } - EXPECT_TRUE(found_linux_gate); - EXPECT_EQ(linux_gate_loc, reinterpret_cast(mapping->start_addr)); - EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG)); - } -} diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc deleted file mode 100644 index 079d85bdc50..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc +++ /dev/null @@ -1,872 +0,0 @@ -// Copyright (c) 2009, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// This code writes out minidump files: -// http://msdn.microsoft.com/en-us/library/ms680378(VS.85,loband).aspx -// -// Minidumps are a Microsoft format which Breakpad uses for recording crash -// dumps. This code has to run in a compromised environment (the address space -// may have received SIGSEGV), thus the following rules apply: -// * You may not enter the dynamic linker. This means that we cannot call -// any symbols in a shared library (inc libc). Because of this we replace -// libc functions in linux_libc_support.h. -// * You may not call syscalls via the libc wrappers. This rule is a subset -// of the first rule but it bears repeating. We have direct wrappers -// around the system calls in linux_syscall_support.h. -// * You may not malloc. There's an alternative allocator in memory.h and -// a canonical instance in the LinuxDumper object. We use the placement -// new form to allocate objects and we don't delete them. - -#include "client/linux/minidump_writer/minidump_writer.h" -#include "client/minidump_file_writer-inl.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "client/minidump_file_writer.h" -#include "google_breakpad/common/minidump_format.h" -#include "google_breakpad/common/minidump_cpu_amd64.h" -#include "google_breakpad/common/minidump_cpu_x86.h" - -#include "client/linux/handler/exception_handler.h" -#include "client/linux/minidump_writer/line_reader.h" -#include "client/linux/minidump_writer//linux_dumper.h" -#include "common/linux/linux_libc_support.h" -#include "common/linux/linux_syscall_support.h" - -// These are additional minidump stream values which are specific to the linux -// breakpad implementation. -enum { - MD_LINUX_CPU_INFO = 0x47670003, /* /proc/cpuinfo */ - MD_LINUX_PROC_STATUS = 0x47670004, /* /proc/$x/status */ - MD_LINUX_LSB_RELEASE = 0x47670005, /* /etc/lsb-release */ - MD_LINUX_CMD_LINE = 0x47670006, /* /proc/$x/cmdline */ - MD_LINUX_ENVIRON = 0x47670007, /* /proc/$x/environ */ - MD_LINUX_AUXV = 0x47670008 /* /proc/$x/auxv */ -}; - -// 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) -typedef MDRawContextX86 RawContextCPU; - -// Write a uint16_t to memory -// out: memory location to write to -// v: value to write. -static void U16(void* out, uint16_t v) { - memcpy(out, &v, sizeof(v)); -} - -// Write a uint32_t to memory -// out: memory location to write to -// v: value to write. -static void U32(void* out, uint32_t v) { - memcpy(out, &v, sizeof(v)); -} - -// Juggle an x86 user_(fp|fpx|)regs_struct into minidump format -// out: the minidump structure -// info: the collection of register structures. -static void CPUFillFromThreadInfo(MDRawContextX86 *out, - const google_breakpad::ThreadInfo &info) { - out->context_flags = MD_CONTEXT_X86_ALL; - - out->dr0 = info.dregs[0]; - out->dr1 = info.dregs[1]; - out->dr2 = info.dregs[2]; - out->dr3 = info.dregs[3]; - // 4 and 5 deliberatly omitted because they aren't included in the minidump - // format. - out->dr6 = info.dregs[6]; - out->dr7 = info.dregs[7]; - - out->gs = info.regs.xgs; - out->fs = info.regs.xfs; - out->es = info.regs.xes; - out->ds = info.regs.xds; - - out->edi = info.regs.edi; - out->esi = info.regs.esi; - out->ebx = info.regs.ebx; - out->edx = info.regs.edx; - out->ecx = info.regs.ecx; - out->eax = info.regs.eax; - - out->ebp = info.regs.ebp; - out->eip = info.regs.eip; - out->cs = info.regs.xcs; - out->eflags = info.regs.eflags; - out->esp = info.regs.esp; - out->ss = info.regs.xss; - - out->float_save.control_word = info.fpregs.cwd; - out->float_save.status_word = info.fpregs.swd; - out->float_save.tag_word = info.fpregs.twd; - out->float_save.error_offset = info.fpregs.fip; - out->float_save.error_selector = info.fpregs.fcs; - out->float_save.data_offset = info.fpregs.foo; - out->float_save.data_selector = info.fpregs.fos; - - // 8 registers * 10 bytes per register. - memcpy(out->float_save.register_area, info.fpregs.st_space, 10 * 8); - - // This matches the Intel fpsave format. - U16(out->extended_registers + 0, info.fpregs.cwd); - U16(out->extended_registers + 2, info.fpregs.swd); - U16(out->extended_registers + 4, info.fpregs.twd); - U16(out->extended_registers + 6, info.fpxregs.fop); - U32(out->extended_registers + 8, info.fpxregs.fip); - U16(out->extended_registers + 12, info.fpxregs.fcs); - U32(out->extended_registers + 16, info.fpregs.foo); - U16(out->extended_registers + 20, info.fpregs.fos); - U32(out->extended_registers + 24, info.fpxregs.mxcsr); - - memcpy(out->extended_registers + 32, &info.fpxregs.st_space, 128); - memcpy(out->extended_registers + 160, &info.fpxregs.xmm_space, 128); -} - -// Juggle an x86 ucontext into minidump format -// out: the minidump structure -// info: the collection of register structures. -static void CPUFillFromUContext(MDRawContextX86 *out, const ucontext *uc, - const struct _libc_fpstate* fp) { - const greg_t* regs = uc->uc_mcontext.gregs; - - out->context_flags = MD_CONTEXT_X86_FULL | - MD_CONTEXT_X86_FLOATING_POINT; - - out->gs = regs[REG_GS]; - out->fs = regs[REG_FS]; - out->es = regs[REG_ES]; - out->ds = regs[REG_DS]; - - out->edi = regs[REG_EDI]; - out->esi = regs[REG_ESI]; - out->ebx = regs[REG_EBX]; - out->edx = regs[REG_EDX]; - out->ecx = regs[REG_ECX]; - out->eax = regs[REG_EAX]; - - out->ebp = regs[REG_EBP]; - out->eip = regs[REG_EIP]; - out->cs = regs[REG_CS]; - out->eflags = regs[REG_EFL]; - out->esp = regs[REG_UESP]; - out->ss = regs[REG_SS]; - - out->float_save.control_word = fp->cw; - out->float_save.status_word = fp->sw; - out->float_save.tag_word = fp->tag; - out->float_save.error_offset = fp->ipoff; - out->float_save.error_selector = fp->cssel; - out->float_save.data_offset = fp->dataoff; - out->float_save.data_selector = fp->datasel; - - // 8 registers * 10 bytes per register. - memcpy(out->float_save.register_area, fp->_st, 10 * 8); -} - -#elif defined(__x86_64) -typedef MDRawContextAMD64 RawContextCPU; - -static void CPUFillFromThreadInfo(MDRawContextAMD64 *out, - const google_breakpad::ThreadInfo &info) { - out->context_flags = MD_CONTEXT_AMD64_FULL | - MD_CONTEXT_AMD64_SEGMENTS; - - out->cs = info.regs.cs; - - out->ds = info.regs.ds; - out->es = info.regs.es; - out->fs = info.regs.fs; - out->gs = info.regs.gs; - - out->ss = info.regs.ss; - out->eflags = info.regs.eflags; - - out->dr0 = info.dregs[0]; - out->dr1 = info.dregs[1]; - out->dr2 = info.dregs[2]; - out->dr3 = info.dregs[3]; - // 4 and 5 deliberatly omitted because they aren't included in the minidump - // format. - out->dr6 = info.dregs[6]; - out->dr7 = info.dregs[7]; - - out->rax = info.regs.rax; - out->rcx = info.regs.rcx; - out->rdx = info.regs.rdx; - out->rbx = info.regs.rbx; - - out->rsp = info.regs.rsp; - - out->rbp = info.regs.rbp; - out->rsi = info.regs.rsi; - out->rdi = info.regs.rdi; - out->r8 = info.regs.r8; - out->r9 = info.regs.r9; - out->r10 = info.regs.r10; - out->r11 = info.regs.r11; - out->r12 = info.regs.r12; - out->r13 = info.regs.r13; - out->r14 = info.regs.r14; - out->r15 = info.regs.r15; - - out->rip = info.regs.rip; - - out->flt_save.control_word = info.fpregs.cwd; - out->flt_save.status_word = info.fpregs.swd; - out->flt_save.tag_word = info.fpregs.ftw; - out->flt_save.error_opcode = info.fpregs.fop; - out->flt_save.error_offset = info.fpregs.rip; - out->flt_save.error_selector = 0; // We don't have this. - out->flt_save.data_offset = info.fpregs.rdp; - out->flt_save.data_selector = 0; // We don't have this. - out->flt_save.mx_csr = info.fpregs.mxcsr; - out->flt_save.mx_csr_mask = info.fpregs.mxcr_mask; - memcpy(&out->flt_save.float_registers, &info.fpregs.st_space, 8 * 16); - memcpy(&out->flt_save.xmm_registers, &info.fpregs.xmm_space, 16 * 16); -} - -static void CPUFillFromUContext(MDRawContextAMD64 *out, const ucontext *uc, - const struct _libc_fpstate* fpregs) { - const greg_t* regs = uc->uc_mcontext.gregs; - - out->context_flags = MD_CONTEXT_AMD64_FULL; - - out->cs = regs[REG_CSGSFS] & 0xffff; - - out->fs = (regs[REG_CSGSFS] >> 32) & 0xffff; - out->gs = (regs[REG_CSGSFS] >> 16) & 0xffff; - - out->eflags = regs[REG_EFL]; - - out->rax = regs[REG_RAX]; - out->rcx = regs[REG_RCX]; - out->rdx = regs[REG_RDX]; - out->rbx = regs[REG_RBX]; - - out->rsp = regs[REG_RSP]; - out->rbp = regs[REG_RBP]; - out->rsi = regs[REG_RSI]; - out->rdi = regs[REG_RDI]; - out->r8 = regs[REG_R8]; - out->r9 = regs[REG_R9]; - out->r10 = regs[REG_R10]; - out->r11 = regs[REG_R11]; - out->r12 = regs[REG_R12]; - out->r13 = regs[REG_R13]; - out->r14 = regs[REG_R14]; - out->r15 = regs[REG_R15]; - - out->rip = regs[REG_RIP]; - - out->flt_save.control_word = fpregs->cwd; - out->flt_save.status_word = fpregs->swd; - out->flt_save.tag_word = fpregs->ftw; - out->flt_save.error_opcode = fpregs->fop; - out->flt_save.error_offset = fpregs->rip; - out->flt_save.data_offset = fpregs->rdp; - out->flt_save.error_selector = 0; // We don't have this. - out->flt_save.data_selector = 0; // We don't have this. - out->flt_save.mx_csr = fpregs->mxcsr; - out->flt_save.mx_csr_mask = fpregs->mxcr_mask; - memcpy(&out->flt_save.float_registers, &fpregs->_st, 8 * 16); - memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16); -} - -#else -#error "This code has not been ported to your platform yet." -#endif - -namespace google_breakpad { - -class MinidumpWriter { - public: - MinidumpWriter(const char* filename, - pid_t crashing_pid, - const ExceptionHandler::CrashContext* context) - : filename_(filename), - siginfo_(&context->siginfo), - ucontext_(&context->context), - float_state_(&context->float_state), - crashing_tid_(context->tid), - dumper_(crashing_pid) { - } - - bool Init() { - return dumper_.Init() && minidump_writer_.Open(filename_) && - dumper_.ThreadsSuspend(); - } - - ~MinidumpWriter() { - minidump_writer_.Close(); - dumper_.ThreadsResume(); - } - - bool Dump() { - // A minidump file contains a number of tagged streams. This is the number - // of stream which we write. - static const unsigned kNumWriters = 11; - - TypedMDRVA header(&minidump_writer_); - TypedMDRVA dir(&minidump_writer_); - if (!header.Allocate()) - return false; - if (!dir.AllocateArray(kNumWriters)) - return false; - memset(header.get(), 0, sizeof(MDRawHeader)); - - header.get()->signature = MD_HEADER_SIGNATURE; - header.get()->version = MD_HEADER_VERSION; - header.get()->time_date_stamp = time(NULL); - header.get()->stream_count = kNumWriters; - header.get()->stream_directory_rva = dir.position(); - - unsigned dir_index = 0; - MDRawDirectory dirent; - - if (!WriteThreadListStream(&dirent)) - return false; - dir.CopyIndex(dir_index++, &dirent); - - if (!WriteMappings(&dirent)) - return false; - dir.CopyIndex(dir_index++, &dirent); - - if (!WriteExceptionStream(&dirent)) - return false; - dir.CopyIndex(dir_index++, &dirent); - - if (!WriteSystemInfoStream(&dirent)) - return false; - dir.CopyIndex(dir_index++, &dirent); - - dirent.stream_type = MD_LINUX_CPU_INFO; - if (!WriteFile(&dirent.location, "/proc/cpuinfo")) - NullifyDirectoryEntry(&dirent); - dir.CopyIndex(dir_index++, &dirent); - - dirent.stream_type = MD_LINUX_PROC_STATUS; - if (!WriteProcFile(&dirent.location, crashing_tid_, "status")) - NullifyDirectoryEntry(&dirent); - dir.CopyIndex(dir_index++, &dirent); - - dirent.stream_type = MD_LINUX_LSB_RELEASE; - if (!WriteFile(&dirent.location, "/etc/lsb-release")) - NullifyDirectoryEntry(&dirent); - dir.CopyIndex(dir_index++, &dirent); - - dirent.stream_type = MD_LINUX_CMD_LINE; - if (!WriteProcFile(&dirent.location, crashing_tid_, "cmdline")) - NullifyDirectoryEntry(&dirent); - dir.CopyIndex(dir_index++, &dirent); - - dirent.stream_type = MD_LINUX_ENVIRON; - if (!WriteProcFile(&dirent.location, crashing_tid_, "environ")) - NullifyDirectoryEntry(&dirent); - dir.CopyIndex(dir_index++, &dirent); - - dirent.stream_type = MD_LINUX_AUXV; - if (!WriteProcFile(&dirent.location, crashing_tid_, "auxv")) - NullifyDirectoryEntry(&dirent); - dir.CopyIndex(dir_index++, &dirent); - - dirent.stream_type = MD_LINUX_AUXV; - if (!WriteProcFile(&dirent.location, crashing_tid_, "maps")) - NullifyDirectoryEntry(&dirent); - dir.CopyIndex(dir_index++, &dirent); - - // If you add more directory entries, don't forget to update kNumWriters, - // above. - - dumper_.ThreadsResume(); - return true; - } - - // Write information about the threads. - bool WriteThreadListStream(MDRawDirectory* dirent) { - const unsigned num_threads = dumper_.threads().size(); - - TypedMDRVA list(&minidump_writer_); - if (!list.AllocateObjectAndArray(num_threads, sizeof(MDRawThread))) - return false; - - dirent->stream_type = MD_THREAD_LIST_STREAM; - dirent->location = list.location(); - - *list.get() = num_threads; - - for (unsigned i = 0; i < num_threads; ++i) { - MDRawThread thread; - my_memset(&thread, 0, sizeof(thread)); - thread.thread_id = dumper_.threads()[i]; - // We have a different source of information for the crashing thread. If - // we used the actual state of the thread we would find it running in the - // signal handler with the alternative stack, which would be deeply - // unhelpful. - if (thread.thread_id == crashing_tid_) { - const void* stack; - size_t stack_len; - if (!dumper_.GetStackInfo(&stack, &stack_len, GetStackPointer())) - return false; - UntypedMDRVA memory(&minidump_writer_); - if (!memory.Allocate(stack_len)) - return false; - uint8_t* stack_copy = (uint8_t*) dumper_.allocator()->Alloc(stack_len); - dumper_.CopyFromProcess(stack_copy, thread.thread_id, stack, stack_len); - memory.Copy(stack_copy, stack_len); - thread.stack.start_of_memory_range = (uintptr_t) (stack); - thread.stack.memory = memory.location(); - TypedMDRVA cpu(&minidump_writer_); - if (!cpu.Allocate()) - return false; - my_memset(cpu.get(), 0, sizeof(RawContextCPU)); - CPUFillFromUContext(cpu.get(), ucontext_, float_state_); - thread.thread_context = cpu.location(); - crashing_thread_context_ = cpu.location(); - } else { - ThreadInfo info; - if (!dumper_.ThreadInfoGet(dumper_.threads()[i], &info)) - return false; - UntypedMDRVA memory(&minidump_writer_); - if (!memory.Allocate(info.stack_len)) - return false; - uint8_t* stack_copy = - (uint8_t*) dumper_.allocator()->Alloc(info.stack_len); - dumper_.CopyFromProcess(stack_copy, thread.thread_id, info.stack, - info.stack_len); - memory.Copy(stack_copy, info.stack_len); - thread.stack.start_of_memory_range = (uintptr_t)(info.stack); - thread.stack.memory = memory.location(); - TypedMDRVA cpu(&minidump_writer_); - if (!cpu.Allocate()) - return false; - my_memset(cpu.get(), 0, sizeof(RawContextCPU)); - CPUFillFromThreadInfo(cpu.get(), info); - thread.thread_context = cpu.location(); - } - - list.CopyIndexAfterObject(i, &thread, sizeof(thread)); - } - - return true; - } - - static bool ShouldIncludeMapping(const MappingInfo& mapping) { - if (mapping.name[0] == 0 || // we only want modules with filenames. - mapping.offset || // we only want to include one mapping per shared lib. - mapping.size < 4096) { // too small to get a signature for. - return false; - } - - return true; - } - - // Write information about the mappings in effect. Because we are using the - // minidump format, the information about the mappings is pretty limited. - // Because of this, we also include the full, unparsed, /proc/$x/maps file in - // another stream in the file. - bool WriteMappings(MDRawDirectory* dirent) { - const unsigned num_mappings = dumper_.mappings().size(); - unsigned num_output_mappings = 0; - - for (unsigned i = 0; i < dumper_.mappings().size(); ++i) { - const MappingInfo& mapping = *dumper_.mappings()[i]; - if (ShouldIncludeMapping(mapping)) - num_output_mappings++; - } - - TypedMDRVA list(&minidump_writer_); - if (!list.AllocateObjectAndArray(num_output_mappings, MD_MODULE_SIZE)) - return false; - - dirent->stream_type = MD_MODULE_LIST_STREAM; - dirent->location = list.location(); - *list.get() = num_output_mappings; - - for (unsigned i = 0, j = 0; i < num_mappings; ++i) { - const MappingInfo& mapping = *dumper_.mappings()[i]; - if (!ShouldIncludeMapping(mapping)) - continue; - - MDRawModule mod; - my_memset(&mod, 0, MD_MODULE_SIZE); - mod.base_of_image = mapping.start_addr; - mod.size_of_image = mapping.size; - const size_t filepath_len = my_strlen(mapping.name); - - // Figure out file name from path - const char* filename_ptr = mapping.name + filepath_len - 1; - while (filename_ptr >= mapping.name) { - if (*filename_ptr == '/') - break; - filename_ptr--; - } - filename_ptr++; - const size_t filename_len = mapping.name + filepath_len - filename_ptr; - - uint8_t cv_buf[MDCVInfoPDB70_minsize + NAME_MAX]; - uint8_t* cv_ptr = cv_buf; - UntypedMDRVA cv(&minidump_writer_); - if (!cv.Allocate(MDCVInfoPDB70_minsize + filename_len + 1)) - return false; - - const uint32_t cv_signature = MD_CVINFOPDB70_SIGNATURE; - memcpy(cv_ptr, &cv_signature, sizeof(cv_signature)); - cv_ptr += sizeof(cv_signature); - - { - // We XOR the first page of the file to get a signature for it. - uint8_t xor_buf[sizeof(MDGUID)]; - size_t done = 0; - uint8_t* signature = cv_ptr; - cv_ptr += sizeof(xor_buf); - - my_memset(signature, 0, sizeof(xor_buf)); - while (done < 4096) { - dumper_.CopyFromProcess(xor_buf, crashing_tid_, - (void *) (mod.base_of_image + done), - sizeof(xor_buf)); - for (unsigned i = 0; i < sizeof(xor_buf); ++i) - signature[i] ^= xor_buf[i]; - done += sizeof(xor_buf); - } - my_memset(cv_ptr, 0, sizeof(uint32_t)); // Set age to 0 on Linux. - cv_ptr += sizeof(uint32_t); - } - - // Write pdb_file_name - memcpy(cv_ptr, filename_ptr, filename_len + 1); - cv.Copy(cv_buf, MDCVInfoPDB70_minsize + filename_len + 1); - - mod.cv_record = cv.location(); - - MDLocationDescriptor ld; - if (!minidump_writer_.WriteString(mapping.name, filepath_len, &ld)) - return false; - mod.module_name_rva = ld.rva; - - list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE); - } - - return true; - } - - bool WriteExceptionStream(MDRawDirectory* dirent) { - TypedMDRVA exc(&minidump_writer_); - if (!exc.Allocate()) - return false; - my_memset(exc.get(), 0, sizeof(MDRawExceptionStream)); - - dirent->stream_type = MD_EXCEPTION_STREAM; - dirent->location = exc.location(); - - exc.get()->thread_id = crashing_tid_; - exc.get()->exception_record.exception_code = siginfo_->si_signo; - exc.get()->exception_record.exception_address = - (uintptr_t) siginfo_->si_addr; - exc.get()->thread_context = crashing_thread_context_; - - return true; - } - - bool WriteSystemInfoStream(MDRawDirectory* dirent) { - TypedMDRVA si(&minidump_writer_); - if (!si.Allocate()) - return false; - my_memset(si.get(), 0, sizeof(MDRawSystemInfo)); - - dirent->stream_type = MD_SYSTEM_INFO_STREAM; - dirent->location = si.location(); - - WriteCPUInformation(si.get()); - WriteOSInformation(si.get()); - - return true; - } - - private: -#if defined(__i386) - uintptr_t GetStackPointer() { - return ucontext_->uc_mcontext.gregs[REG_ESP]; - } -#elif defined(__x86_64) - uintptr_t GetStackPointer() { - return ucontext_->uc_mcontext.gregs[REG_RSP]; - } -#else -#error "This code has not been ported to your platform yet." -#endif - - void NullifyDirectoryEntry(MDRawDirectory* dirent) { - dirent->stream_type = 0; - dirent->location.data_size = 0; - dirent->location.rva = 0; - } - - 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; - int value; - bool found; - } cpu_info_table[] = { - { "processor", -1, false }, - { "model", 0, false }, - { "stepping", 0, false }, - { "cpu family", 0, false }, - }; - - // processor_architecture should always be set, do this first - sys_info->processor_architecture = -#if defined(__i386) - MD_CPU_ARCHITECTURE_X86; -#elif defined(__x86_64) - MD_CPU_ARCHITECTURE_AMD64; -#else -#error "Unknown CPU arch" -#endif - - const int fd = sys_open("/proc/cpuinfo", O_RDONLY, 0); - if (fd < 0) - return false; - - { - PageAllocator allocator; - LineReader* const line_reader = new(allocator) LineReader(fd); - const char* line; - unsigned line_len; - while (line_reader->GetNextLine(&line, &line_len)) { - 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) - continue; - if (!strncmp(line, entry->info_name, strlen(entry->info_name))) { - const char* value = 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 + strlen(entry->info_name); - for (; space_ptr < value; space_ptr++) { - if (!isspace(*space_ptr)) { - break; - } - } - if (space_ptr != value) - continue; - - sscanf(++value, " %d", &(entry->value)); - entry->found = true; - } - } - - // special case for vendor_id - if (!strncmp(line, vendor_id_name, vendor_id_name_length)) { - const char* value = strchr(line, ':'); - if (!value) - goto popline; - - // skip ':" and all the spaces that follows - do { - value++; - } while (isspace(*value)); - - if (*value) { - size_t length = strlen(value); - if (length == 0) - goto popline; - // we don't want the trailing newline - if (value[length - 1] == '\n') - length--; - // ensure we have space for the value - if (length < sizeof(vendor_id)) - strncpy(vendor_id, value, length); - } - } - -popline: - line_reader->PopLine(line_len); - } - sys_close(fd); - } - - // make sure we got everything we wanted - for (size_t i = 0; - i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]); - i++) { - if (!cpu_info_table[i].found) { - return false; - } - } - // /proc/cpuinfo contains cpu id, change it into number by adding one. - cpu_info_table[0].value++; - - sys_info->number_of_processors = cpu_info_table[0].value; - sys_info->processor_level = cpu_info_table[3].value; - sys_info->processor_revision = cpu_info_table[1].value << 8 | - cpu_info_table[2].value; - - if (vendor_id[0] != '\0') { - memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id, - sizeof(sys_info->cpu.x86_cpu_info.vendor_id)); - } - return true; - } - - bool WriteFile(MDLocationDescriptor* result, const char* filename) { - const int fd = sys_open(filename, O_RDONLY, 0); - if (fd < 0) - return false; - - // We can't stat the files because several of the files that we want to - // read are kernel seqfiles, which always have a length of zero. So we have - // to read as much as we can into a buffer. - static const unsigned kMaxFileSize = 1024; - uint8_t* data = (uint8_t*) dumper_.allocator()->Alloc(kMaxFileSize); - - size_t done = 0; - while (done < kMaxFileSize) { - ssize_t r; - do { - r = sys_read(fd, data + done, kMaxFileSize - done); - } while (r == -1 && errno == EINTR); - - if (r < 1) - break; - done += r; - } - sys_close(fd); - - if (!done) - return false; - - UntypedMDRVA memory(&minidump_writer_); - if (!memory.Allocate(done)) - return false; - memory.Copy(data, done); - *result = memory.location(); - return true; - } - - bool WriteOSInformation(MDRawSystemInfo* sys_info) { - sys_info->platform_id = MD_OS_LINUX; - - struct utsname uts; - if (uname(&uts)) - return false; - - static const size_t buf_len = 512; - char buf[buf_len] = {0}; - size_t space_left = buf_len - 1; - const char* info_table[] = { - uts.sysname, - uts.release, - uts.version, - uts.machine, - NULL - }; - bool first_item = true; - for (const char** cur_info = info_table; *cur_info; cur_info++) { - static const char* separator = " "; - size_t separator_len = strlen(separator); - size_t info_len = strlen(*cur_info); - if (info_len == 0) - continue; - - if (space_left < info_len + (first_item ? 0 : separator_len)) - break; - - if (!first_item) { - strcat(buf, separator); - space_left -= separator_len; - } - - first_item = false; - strcat(buf, *cur_info); - space_left -= info_len; - } - - MDLocationDescriptor location; - if (!minidump_writer_.WriteString(buf, 0, &location)) - return false; - sys_info->csd_version_rva = location.rva; - - return true; - } - - bool WriteProcFile(MDLocationDescriptor* result, pid_t pid, - const char* filename) { - char buf[80]; - memcpy(buf, "/proc/", 6); - const unsigned pid_len = my_int_len(pid); - my_itos(buf + 6, pid, pid_len); - buf[6 + pid_len] = '/'; - memcpy(buf + 6 + pid_len + 1, filename, my_strlen(filename) + 1); - return WriteFile(result, buf); - } - - const char* const filename_; // output filename - const siginfo_t* const siginfo_; // from the signal handler (see sigaction) - const struct ucontext* const ucontext_; // also from the signal handler - const struct _libc_fpstate* const float_state_; // ditto - const pid_t crashing_tid_; // the process which actually crashed - LinuxDumper dumper_; - MinidumpFileWriter minidump_writer_; - MDLocationDescriptor crashing_thread_context_; -}; - -bool WriteMinidump(const char* filename, pid_t crashing_process, - const void* blob, size_t blob_size) { - if (blob_size != sizeof(ExceptionHandler::CrashContext)) - return false; - const ExceptionHandler::CrashContext* context = - reinterpret_cast(blob); - MinidumpWriter writer(filename, crashing_process, context); - if (!writer.Init()) - return false; - return writer.Dump(); -} - -} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/sender/google_crash_report_sender.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/sender/google_crash_report_sender.cc deleted file mode 100644 index da9661da25a..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/sender/google_crash_report_sender.cc +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) 2009, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "common/linux/google_crashdump_uploader.h" -#include "third_party/linux/include/glog/logging.h" -#include "third_party/linux/include/gflags/gflags.h" -#include - -DEFINE_string(crash_server, "http://clients2.google.com/cr", - "The crash server to upload minidumps to."); -DEFINE_string(product_name, "", - "The product name that the minidump corresponds to."); -DEFINE_string(product_version, "", - "The version of the product that produced the minidump."); -DEFINE_string(client_id, "", - "The client GUID"); -DEFINE_string(minidump_path, "", - "The path of the minidump file."); -DEFINE_string(ptime, "", - "The process uptime in milliseconds."); -DEFINE_string(ctime, "", - "The cumulative process uptime in milliseconds."); -DEFINE_string(email, "", - "The user's email address."); -DEFINE_string(comments, "", - "Extra user comments"); -DEFINE_string(proxy_host, "", - "Proxy host"); -DEFINE_string(proxy_userpasswd, "", - "Proxy username/password in user:pass format."); - - -bool CheckForRequiredFlagsOrDie() { - std::string error_text = ""; - if (FLAGS_product_name.empty()) { - error_text.append("\nProduct name must be specified."); - } - - if (FLAGS_product_version.empty()) { - error_text.append("\nProduct version must be specified."); - } - - if (FLAGS_client_id.empty()) { - error_text.append("\nClient ID must be specified."); - } - - if (FLAGS_minidump_path.empty()) { - error_text.append("\nMinidump pathname must be specified."); - } - - if (!error_text.empty()) { - LOG(ERROR) << error_text; - return false; - } - return true; -} - -int main(int argc, char *argv[]) { - google::InitGoogleLogging(argv[0]); - google::ParseCommandLineFlags(&argc, &argv, true); - if (!CheckForRequiredFlagsOrDie()) { - return 1; - } - google_breakpad::GoogleCrashdumpUploader g(FLAGS_product_name, - FLAGS_product_version, - FLAGS_client_id, - FLAGS_ptime, - FLAGS_ctime, - FLAGS_email, - FLAGS_comments, - FLAGS_minidump_path, - FLAGS_crash_server, - FLAGS_proxy_host, - FLAGS_proxy_userpasswd); - g.Upload(); -} diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/Breakpad.xcodeproj/project.pbxproj b/toolkit/crashreporter/google-breakpad/src/client/mac/Breakpad.xcodeproj/project.pbxproj index aaeb9a8ec8b..97659d3219a 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/Breakpad.xcodeproj/project.pbxproj +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/Breakpad.xcodeproj/project.pbxproj @@ -19,9 +19,6 @@ F945858E0F782333009A47BF /* PBXTargetDependency */, F94585900F782336009A47BF /* PBXTargetDependency */, F93DE3A70F830D1D00608B94 /* PBXTargetDependency */, - F95BB8B3101F94D300AA053B /* PBXTargetDependency */, - F95BB8B5101F94D300AA053B /* PBXTargetDependency */, - F95BB8B7101F94D300AA053B /* PBXTargetDependency */, ); name = All; productName = All; @@ -91,7 +88,6 @@ F93DE33F0F82C66B00608B94 /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53820ECCE635009BE4BA /* string_utilities.cc */; }; F93DE3410F82C68300608B94 /* exception_handler_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F93DE3400F82C68300608B94 /* exception_handler_test.cc */; }; F945849E0F280E3C009A47BF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F945849C0F280E3C009A47BF /* Localizable.strings */; }; - F9B630A0100FF96B00D0F4AC /* goArrow.png in Resources */ = {isa = PBXBuildFile; fileRef = F9B6309F100FF96B00D0F4AC /* goArrow.png */; }; F9C44DB20EF07288003AEBAA /* Controller.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C44DAC0EF07288003AEBAA /* Controller.m */; }; F9C44DB30EF07288003AEBAA /* crashduringload in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DAD0EF07288003AEBAA /* crashduringload */; }; F9C44DB40EF07288003AEBAA /* crashInMain in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DAE0EF07288003AEBAA /* crashInMain */; }; @@ -195,55 +191,6 @@ remoteGlobalIDString = F9C44DA40EF060A8003AEBAA; remoteInfo = BreakpadTest; }; - F95BB884101F949F00AA053B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F95BB87C101F949F00AA053B /* crash_report.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 8DD76FA10486AA7600D96B5E /* crash_report */; - remoteInfo = crash_report; - }; - F95BB891101F94AC00AA053B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 8DD76FA10486AA7600D96B5E /* dump_syms */; - remoteInfo = dump_syms; - }; - F95BB89E101F94C000AA053B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F95BB894101F94C000AA053B /* symupload.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 8DD76FA10486AA7600D96B5E /* symupload */; - remoteInfo = symupload; - }; - F95BB8A0101F94C000AA053B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F95BB894101F94C000AA053B /* symupload.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 9BD835FB0B0544950055103E /* minidump_upload */; - remoteInfo = minidump_upload; - }; - F95BB8B2101F94D300AA053B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = 8DD76F960486AA7600D96B5E /* dump_syms */; - remoteInfo = dump_syms; - }; - F95BB8B4101F94D300AA053B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F95BB894101F94C000AA053B /* symupload.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = 8DD76F960486AA7600D96B5E /* symupload */; - remoteInfo = symupload; - }; - F95BB8B6101F94D300AA053B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F95BB87C101F949F00AA053B /* crash_report.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = 8DD76F960486AA7600D96B5E /* crash_report */; - remoteInfo = crash_report; - }; F9C44E190EF0790F003AEBAA /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; @@ -341,10 +288,6 @@ F93DE3400F82C68300608B94 /* exception_handler_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = exception_handler_test.cc; path = handler/exception_handler_test.cc; sourceTree = ""; }; F945849D0F280E3C009A47BF /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = sender/English.lproj/Localizable.strings; sourceTree = ""; }; F945859D0F78241E009A47BF /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Framework/Info.plist; sourceTree = ""; }; - F95BB87C101F949F00AA053B /* crash_report.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = crash_report.xcodeproj; path = ../../tools/mac/crash_report/crash_report.xcodeproj; sourceTree = SOURCE_ROOT; }; - F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = dump_syms.xcodeproj; path = ../../tools/mac/dump_syms/dump_syms.xcodeproj; sourceTree = SOURCE_ROOT; }; - F95BB894101F94C000AA053B /* symupload.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = symupload.xcodeproj; path = ../../tools/mac/symupload/symupload.xcodeproj; sourceTree = SOURCE_ROOT; }; - F9B6309F100FF96B00D0F4AC /* goArrow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = goArrow.png; path = sender/goArrow.png; sourceTree = ""; }; F9C44DA50EF060A8003AEBAA /* BreakpadTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BreakpadTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; F9C44DAC0EF07288003AEBAA /* Controller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Controller.m; path = testapp/Controller.m; sourceTree = ""; }; F9C44DAD0EF07288003AEBAA /* crashduringload */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = crashduringload; path = testapp/crashduringload; sourceTree = ""; }; @@ -461,7 +404,6 @@ 0867D691FE84028FC02AAC07 /* Breakpad */ = { isa = PBXGroup; children = ( - F95BB8A3101F94C300AA053B /* Tools */, 32DBCF5E0370ADEE00C91783 /* Breakpad_Prefix.pch */, F92C538D0ECCE6F2009BE4BA /* client */, F92C53600ECCE3D6009BE4BA /* common */, @@ -593,7 +535,6 @@ F92C56A60ECE04B6009BE4BA /* sender */ = { isa = PBXGroup; children = ( - F9B6309F100FF96B00D0F4AC /* goArrow.png */, F92C56A70ECE04C5009BE4BA /* crash_report_sender.h */, F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */, F945849C0F280E3C009A47BF /* Localizable.strings */, @@ -605,41 +546,6 @@ name = sender; sourceTree = ""; }; - F95BB87D101F949F00AA053B /* Products */ = { - isa = PBXGroup; - children = ( - F95BB885101F949F00AA053B /* crash_report */, - ); - name = Products; - sourceTree = ""; - }; - F95BB88A101F94AC00AA053B /* Products */ = { - isa = PBXGroup; - children = ( - F95BB892101F94AC00AA053B /* dump_syms */, - ); - name = Products; - sourceTree = ""; - }; - F95BB895101F94C000AA053B /* Products */ = { - isa = PBXGroup; - children = ( - F95BB89F101F94C000AA053B /* symupload */, - F95BB8A1101F94C000AA053B /* minidump_upload */, - ); - name = Products; - sourceTree = ""; - }; - F95BB8A3101F94C300AA053B /* Tools */ = { - isa = PBXGroup; - children = ( - F95BB894101F94C000AA053B /* symupload.xcodeproj */, - F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */, - F95BB87C101F949F00AA053B /* crash_report.xcodeproj */, - ); - name = Tools; - sourceTree = ""; - }; F9C44DAB0EF0726F003AEBAA /* testapp */ = { isa = PBXGroup; children = ( @@ -872,20 +778,6 @@ mainGroup = 0867D691FE84028FC02AAC07 /* Breakpad */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; - projectReferences = ( - { - ProductGroup = F95BB87D101F949F00AA053B /* Products */; - ProjectRef = F95BB87C101F949F00AA053B /* crash_report.xcodeproj */; - }, - { - ProductGroup = F95BB88A101F94AC00AA053B /* Products */; - ProjectRef = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; - }, - { - ProductGroup = F95BB895101F94C000AA053B /* Products */; - ProjectRef = F95BB894101F94C000AA053B /* symupload.xcodeproj */; - }, - ); projectRoot = ""; targets = ( 8DC2EF4F0486A6940098B216 /* Breakpad */, @@ -902,37 +794,6 @@ }; /* End PBXProject section */ -/* Begin PBXReferenceProxy section */ - F95BB885101F949F00AA053B /* crash_report */ = { - isa = PBXReferenceProxy; - fileType = "compiled.mach-o.executable"; - path = crash_report; - remoteRef = F95BB884101F949F00AA053B /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - F95BB892101F94AC00AA053B /* dump_syms */ = { - isa = PBXReferenceProxy; - fileType = "compiled.mach-o.executable"; - path = dump_syms; - remoteRef = F95BB891101F94AC00AA053B /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - F95BB89F101F94C000AA053B /* symupload */ = { - isa = PBXReferenceProxy; - fileType = "compiled.mach-o.executable"; - path = symupload; - remoteRef = F95BB89E101F94C000AA053B /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - F95BB8A1101F94C000AA053B /* minidump_upload */ = { - isa = PBXReferenceProxy; - fileType = "compiled.mach-o.executable"; - path = minidump_upload; - remoteRef = F95BB8A0101F94C000AA053B /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - /* Begin PBXResourcesBuildPhase section */ 8DC2EF520486A6940098B216 /* Resources */ = { isa = PBXResourcesBuildPhase; @@ -952,7 +813,6 @@ 4084699D0F5D9CF900FDCA37 /* crash_report_sender.icns in Resources */, 33880C800F9E097100817F82 /* InfoPlist.strings in Resources */, 3329D4ED0FA16D820007BBC5 /* Breakpad.nib in Resources */, - F9B630A0100FF96B00D0F4AC /* goArrow.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1203,21 +1063,6 @@ target = F9C44DA40EF060A8003AEBAA /* BreakpadTest */; targetProxy = F945858F0F782336009A47BF /* PBXContainerItemProxy */; }; - F95BB8B3101F94D300AA053B /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = dump_syms; - targetProxy = F95BB8B2101F94D300AA053B /* PBXContainerItemProxy */; - }; - F95BB8B5101F94D300AA053B /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = symupload; - targetProxy = F95BB8B4101F94D300AA053B /* PBXContainerItemProxy */; - }; - F95BB8B7101F94D300AA053B /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = crash_report; - targetProxy = F95BB8B6101F94D300AA053B /* PBXContainerItemProxy */; - }; F9C44E1A0EF0790F003AEBAA /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 8DC2EF4F0486A6940098B216 /* Breakpad */; diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.h b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.h index 6b5ce66ac89..ab4d6220057 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.h +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.h @@ -89,7 +89,6 @@ extern "C" { #define BREAKPAD_PROCESS_CRASH_TIME "BreakpadProcessCrashTime" #define BREAKPAD_LOGFILE_KEY_PREFIX "BreakpadAppLogFile" #define BREAKPAD_SERVER_PARAMETER_PREFIX "BreakpadServerParameterPrefix_" -#define BREAKPAD_ON_DEMAND "BreakpadOnDemand" // Optional user-defined function to dec to decide if we should handle // this crash or forward it along. @@ -216,7 +215,7 @@ typedef bool (*BreakpadFilterCallback)(int exception_type, //============================================================================= // The following are NOT user-supplied but are documented here for // completeness. They are calculated by Breakpad during initialization & -// crash-dump generation, or entered in by the user. +// crash-dump generation. // // BREAKPAD_PROCESS_START_TIME The time the process started. // @@ -243,12 +242,6 @@ typedef bool (*BreakpadFilterCallback)(int exception_type, // server without leaking Breakpad's // internal values. // -// BREAKPAD_ON_DEMAND Used internally to indicate to the -// Reporter that we're sending on-demand, -// not as result of a crash. -// -// BREAKPAD_COMMENTS The text the user provided as comments. -// Only used in crash_report_sender. // Returns a new BreakpadRef object on success, NULL otherwise. BreakpadRef BreakpadCreate(NSDictionary *parameters); @@ -293,7 +286,7 @@ void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key); // necessary. Note that as mentioned above there are limits on both // the number of keys and their length. void BreakpadAddUploadParameter(BreakpadRef ref, NSString *key, - NSString *value); + NSString *value); // This method will remove a previously-added parameter from the // upload parameter set. diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm index 23e5d9bfc6c..551f9785099 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm @@ -409,6 +409,7 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) { NSString *timeout = [parameters objectForKey:@BREAKPAD_CONFIRM_TIMEOUT]; NSArray *logFilePaths = [parameters objectForKey:@BREAKPAD_LOGFILES]; NSString *logFileTailSize = [parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE]; + NSString *reportEmail = [parameters objectForKey:@BREAKPAD_EMAIL]; NSString *requestUserText = [parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS]; NSString *requestEmail = [parameters objectForKey:@BREAKPAD_REQUEST_EMAIL]; @@ -450,7 +451,7 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) { vendor = @"Vendor not specified"; } - // Normalize the values. + // Normalize the values if (skipConfirm) { skipConfirm = [skipConfirm uppercaseString]; @@ -503,7 +504,7 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) { [resourcePath stringByAppendingPathComponent:@"Inspector"]; } - // Verify that there is an Inspector tool. + // Verify that there is an Inspector tool if (![[NSFileManager defaultManager] fileExistsAtPath:inspectorPathString]) { DEBUGLOG(stderr, "Cannot find Inspector tool\n"); return false; @@ -516,7 +517,7 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) { reporterPathString = [[NSBundle bundleWithPath:reporterPathString] executablePath]; } - // Verify that there is a Reporter application. + // Verify that there is a Reporter application if (![[NSFileManager defaultManager] fileExistsAtPath:reporterPathString]) { DEBUGLOG(stderr, "Cannot find Reporter tool\n"); @@ -587,6 +588,11 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) { } } + if (reportEmail) { + dictionary.SetKeyValue(BREAKPAD_EMAIL, + [reportEmail UTF8String]); + } + if (serverParameters) { // For each key-value pair, call BreakpadAddUploadParameter() NSEnumerator *keyEnumerator = [serverParameters keyEnumerator]; @@ -627,9 +633,7 @@ void Breakpad::RemoveKeyValue(NSString *key) { //============================================================================= void Breakpad::GenerateAndSendReport() { - config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "YES"); HandleException(0, 0, 0, mach_thread_self()); - config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "NO"); } //============================================================================= @@ -747,59 +751,65 @@ BreakpadRef BreakpadCreate(NSDictionary *parameters) { // Create a mutex for use in accessing the SimpleStringDictionary int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL); - if (mutexResult == 0) { - - // With the current compiler, gBreakpadAllocator is allocating 1444 bytes. - // Let's round up to the nearest page size. - // - int breakpad_pool_size = 4096; - - /* - sizeof(Breakpad) - + sizeof(google_breakpad::ExceptionHandler) - + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler ) - */ - - gBreakpadAllocator = - new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator))) - ProtectedMemoryAllocator(breakpad_pool_size); - - // Stack-based autorelease pool for Breakpad::Create() obj-c code. - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - Breakpad *breakpad = Breakpad::Create(parameters); - - if (breakpad) { - // Make read-only to protect against memory smashers - gMasterAllocator->Protect(); - gKeyValueAllocator->Protect(); - gBreakpadAllocator->Protect(); - // Can uncomment this line to figure out how much space was actually - // allocated using this allocator - // printf("gBreakpadAllocator allocated size = %d\n", - // gBreakpadAllocator->GetAllocatedSize() ); - [pool release]; - return (BreakpadRef)breakpad; - } - - [pool release]; + if (mutexResult != 0) { + throw mutexResult; // caught down below } + + // With the current compiler, gBreakpadAllocator is allocating 1444 bytes. + // Let's round up to the nearest page size. + // + int breakpad_pool_size = 4096; + + /* + sizeof(Breakpad) + + sizeof(google_breakpad::ExceptionHandler) + + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler ) + */ + + gBreakpadAllocator = + new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator))) + ProtectedMemoryAllocator(breakpad_pool_size); + + // Stack-based autorelease pool for Breakpad::Create() obj-c code. + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + Breakpad *breakpad = Breakpad::Create(parameters); + + if (breakpad) { + // Make read-only to protect against memory smashers + gMasterAllocator->Protect(); + gKeyValueAllocator->Protect(); + gBreakpadAllocator->Protect(); + } else { + [pool release]; +#ifdef __EXCEPTIONS + throw(-1); +#else + return NULL; +#endif + } + + // Can uncomment this line to figure out how much space was actually + // allocated using this allocator + // printf("gBreakpadAllocator allocated size = %d\n", + // gBreakpadAllocator->GetAllocatedSize() ); + + [pool release]; + return (BreakpadRef)breakpad; } catch(...) { // don't let exceptions leave this C API - fprintf(stderr, "BreakpadCreate() : error\n"); - } + if (gKeyValueAllocator) { + gKeyValueAllocator->~ProtectedMemoryAllocator(); + gKeyValueAllocator = NULL; + } - if (gKeyValueAllocator) { - gKeyValueAllocator->~ProtectedMemoryAllocator(); - gKeyValueAllocator = NULL; - } + if (gBreakpadAllocator) { + gBreakpadAllocator->~ProtectedMemoryAllocator(); + gBreakpadAllocator = NULL; + } - if (gBreakpadAllocator) { - gBreakpadAllocator->~ProtectedMemoryAllocator(); - gBreakpadAllocator = NULL; + delete gMasterAllocator; + gMasterAllocator = NULL; } - delete gMasterAllocator; - gMasterAllocator = NULL; - return NULL; } diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/Inspector.mm b/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/Inspector.mm index d328ee5dbf6..5c5c2218dbd 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/Inspector.mm +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/Inspector.mm @@ -319,12 +319,6 @@ kern_return_t Inspector::ReadMessages() { printf("parameter count = %d\n", info.parameter_count); #endif - // In certain situations where multiple crash requests come - // through quickly, we can end up with the mach IPC messages not - // coming through correctly. Since we don't know what parameters - // we've missed, we can't do much besides abort the crash dump - // situation in this case. - unsigned int parameters_read = 0; // The initial message contains the number of key value pairs that // we are expected to read. // Read each key/value pair, one mach message per key/value pair. @@ -335,14 +329,6 @@ kern_return_t Inspector::ReadMessages() { if(result == KERN_SUCCESS) { KeyValueMessageData &key_value_data = (KeyValueMessageData&)*message.GetData(); - // If we get a blank key, make sure we don't increment the - // parameter count; in some cases (notably on-demand generation - // many times in a short period of time) caused the Mach IPC - // messages to not come through correctly. - if (strlen(key_value_data.key) == 0) { - continue; - } - parameters_read++; config_params_.SetKeyValue(key_value_data.key, key_value_data.value); } else { @@ -350,11 +336,6 @@ kern_return_t Inspector::ReadMessages() { break; } } - if (parameters_read != info.parameter_count) { - DEBUGLOG(stderr, "Only read %d parameters instead of %d, aborting crash " - "dump generation.", parameters_read, info.parameter_count); - return KERN_FAILURE; - } } return result; @@ -398,7 +379,7 @@ bool Inspector::InspectTask() { SetCrashTimeParameters(); // If the client app has not specified a minidump directory, // use a default of Library// - if (!minidumpDirectory || 0 == strlen(minidumpDirectory)) { + if (0 == strlen(minidumpDirectory)) { NSArray *libraryDirectories = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc index 9886a5293d7..8c4a0d501f1 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc @@ -92,15 +92,12 @@ extern "C" boolean_t exc_server(mach_msg_header_t *request, mach_msg_header_t *reply); - // This symbol must be visible to dlsym() - see - // http://code.google.com/p/google-breakpad/issues/detail?id=345 for details. kern_return_t catch_exception_raise(mach_port_t target_port, mach_port_t failed_thread, mach_port_t task, exception_type_t exception, exception_data_t code, - mach_msg_type_number_t code_count) - __attribute__((visibility("default"))); + mach_msg_type_number_t code_count); kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread, @@ -438,9 +435,6 @@ kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread, exception_type_t exception, exception_data_t code, mach_msg_type_number_t code_count) { - if (task != mach_task_self()) { - return KERN_FAILURE; - } return ForwardException(task, failed_thread, exception, code, code_count); } diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.cc b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.cc index f8dc8eaa362..e91a39520a5 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.cc +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.cc @@ -637,7 +637,6 @@ bool MinidumpGenerator::WriteSystemInfoStream( // get version and feature info cpuid(1, info_ptr->cpu.x86_cpu_info.version_information, unused, unused2, info_ptr->cpu.x86_cpu_info.feature_information); - // family info_ptr->processor_level = (info_ptr->cpu.x86_cpu_info.version_information & 0xF00) >> 8; @@ -645,20 +644,6 @@ bool MinidumpGenerator::WriteSystemInfoStream( info_ptr->processor_revision = (info_ptr->cpu.x86_cpu_info.version_information & 0xF) | ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0) << 4); - - // decode extended model info - if (info_ptr->processor_level == 0xF || - info_ptr->processor_level == 0x6) { - info_ptr->processor_revision |= - ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0000) >> 4); - } - - // decode extended family info - if (info_ptr->processor_level == 0xF) { - info_ptr->processor_level += - ((info_ptr->cpu.x86_cpu_info.version_information & 0xFF00000) >> 20); - } - #endif // __i386__ break; default: diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h index b065fba956a..8b4f327e1b2 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h @@ -68,7 +68,7 @@ typedef MDRawContextPPC MinidumpContext; // Use the REGISTER_FROM_THREADSTATE to access a register name from the // breakpad_thread_state_t structure. -#if __DARWIN_UNIX03 || TARGET_CPU_X86_64 || TARGET_CPU_PPC64 +#if __DARWIN_UNIX03 || !TARGET_CPU_X86 || TARGET_CPU_X86_64 // In The 10.5 SDK Headers Apple prepended __ to the variable names in the // i386_thread_state_t structure. There's no good way to tell what version of // the SDK we're compiling against so we just toggle on the same preprocessor diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/classes.nib b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/classes.nib index c9d43a3871a..ebc5ab40e73 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/classes.nib +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/classes.nib @@ -4,14 +4,6 @@ IBClasses - - CLASS - LengthLimitingTextField - LANGUAGE - ObjC - SUPERCLASS - NSTextField - ACTIONS @@ -34,14 +26,10 @@ NSButton commentMessage_ NSTextField - commentsEntryField_ - LengthLimitingTextField - countdownLabel_ - NSTextField dialogTitle_ NSTextField emailEntryField_ - LengthLimitingTextField + NSView emailLabel_ NSTextField emailMessage_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/info.nib b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/info.nib index e39a8e54d33..21bd6319e32 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/info.nib +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/info.nib @@ -3,17 +3,17 @@ IBFramework Version - 676 + 672 IBLastKnownRelativeProjectPath ../Breakpad.xcodeproj IBOldestOS 5 IBOpenObjects - 132 + 2 IBSystem Version - 9J61 + 9G55 targetFramework IBCocoaFramework diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/keyedobjects.nib b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/keyedobjects.nib index e370206c166426f1ec3ff2a9578fe891ac3a5190..d0cbd3f24e2a9e3bb76f28d9c4aadec5398cdac7 100644 GIT binary patch literal 13747 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 diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/English.lproj/Localizable.strings b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/English.lproj/Localizable.strings index 70626567c95f166a7ac0bf7d3b89d695264e4b1b..efc832acfbb8b635940dedec2d123ae8975e009f 100644 GIT binary patch delta 90 zcmca7c!7IDl3)fyDnlYe3Xm*faAU}2D4BebNqFM4Ansg-Vup0M(EZ7e7+rXk81fkM m8G?a|^B7Vl{%vMfVzA!)oOv?i*Hh*K)VFUod`Wv(W delta 702 zcmcb>eNS*gl43GL5kn$FF+&D}3qvN5mBWzFkj~)2kP2j{0Lh|>#i^oL6-^dqYMz|H zxJ;9aL5U#`sLh$MQHI{BK&8n*#ic;H1Y|xCr!Xi0ZOa3aAbt^0wunIwXs9&y?QCv2cp#ZGA3}}u5gDQg&g9?KK%%sgTnI`3P4q<47os+ znLxcDoDQ=er~}EidYsk;L$#FxElUI%htEQM)`5Z!$wCzSd>QINZYu@H0Dk}Ff}I0X YiO)iO)*);M1& diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.h b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.h index ca5b30797e6..f62e7613c91 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.h +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.h @@ -41,24 +41,6 @@ extern NSString *const kGoogleServerType; extern NSString *const kSocorroServerType; extern NSString *const kDefaultServerType; - -// We're sublcassing NSTextField in order to override a particular -// method (see the implementation) that lets us reject changes if they -// are longer than a particular length. Bindings would normally solve -// this problem, but when we implemented a validation method, and -// returned NO for strings that were too long, the UI was not updated -// right away, which was a poor user experience. The UI would be -// updated as soon as the text field lost first responder status, -// which isn't soon enough. It is a known bug that the UI KVO didn't -// work in the middle of a validation. -@interface LengthLimitingTextField : NSTextField { - @private - unsigned int maximumLength_; -} - -- (void) setMaximumLength:(unsigned int)maxLength; -@end - @interface Reporter : NSObject { @public IBOutlet NSWindow *alertWindow_; // The alert window @@ -68,34 +50,26 @@ extern NSString *const kDefaultServerType; IBOutlet NSBox *preEmailBox_; IBOutlet NSBox *emailSectionBox_; // Localized elements (or things that need to be moved during localization). - IBOutlet NSTextField *dialogTitle_; - IBOutlet NSTextField *commentMessage_; - IBOutlet NSTextField *emailMessage_; - IBOutlet NSTextField *emailLabel_; - IBOutlet NSTextField *privacyLinkLabel_; - IBOutlet NSButton *sendButton_; - IBOutlet NSButton *cancelButton_; - IBOutlet LengthLimitingTextField *emailEntryField_; - IBOutlet LengthLimitingTextField *commentsEntryField_; - IBOutlet NSTextField *countdownLabel_; - IBOutlet NSView *privacyLinkArrow_; + IBOutlet NSTextField *dialogTitle_; + IBOutlet NSTextField *commentMessage_; + IBOutlet NSTextField *emailMessage_; + IBOutlet NSTextField *emailLabel_; + IBOutlet NSTextField *privacyLinkLabel_; + IBOutlet NSButton *sendButton_; + IBOutlet NSButton *cancelButton_; + IBOutlet NSView *emailEntryField_; + IBOutlet NSView *privacyLinkArrow_; // Text field bindings, for user input. NSString *commentsValue_; // Comments from the user NSString *emailValue_; // Email from the user - NSString *countdownMessage_; // Message indicating time - // left for input. + @private int configFile_; // File descriptor for config file NSMutableDictionary *parameters_; // Key value pairs of data (STRONG) NSData *minidumpContents_; // The data in the minidump (STRONG) NSData *logFileData_; // An NSdata for the tar, // bz2'd log file. - NSTimeInterval remainingDialogTime_; // Keeps track of how long - // we have until we cancel - // the dialog - NSTimer *messageTimer_; // Timer we use to update - // the dialog NSMutableDictionary *serverDictionary_; // The dictionary mapping a // server type name to a // dictionary of server @@ -133,7 +107,4 @@ extern NSString *const kDefaultServerType; - (NSString *)emailValue; - (void)setEmailValue:(NSString *)value; -- (NSString *)countdownMessage; -- (void)setCountdownMessage:(NSString *)value; - @end diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.m b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.m index 0cdeb67642e..5d76290cef8 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.m +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.m @@ -42,11 +42,8 @@ #define kLastSubmission @"LastSubmission" const int kMinidumpFileLengthLimit = 800000; -const int kUserCommentsMaxLength = 1500; -const int kEmailMaxLength = 64; -#define kApplePrefsSyncExcludeAllKey \ - @"com.apple.PreferenceSync.ExcludeAllSyncKeys" +#define kApplePrefsSyncExcludeAllKey @"com.apple.PreferenceSync.ExcludeAllSyncKeys" NSString *const kGoogleServerType = @"google"; NSString *const kSocorroServerType = @"socorro"; @@ -112,12 +109,9 @@ NSString *const kDefaultServerType = @"google"; @implementation NSTextField (ResizabilityExtentions) - (float)breakpad_adjustHeightToFit { NSRect oldFrame = [self frame]; - // Starting with the 10.5 SDK, height won't grow, so make it huge to start. - NSRect presizeFrame = oldFrame; - presizeFrame.size.height = MAXFLOAT; // sizeToFit will blow out the width rather than making the field taller, so // we do it manually. - NSSize newSize = [[self cell] cellSizeForBounds:presizeFrame]; + NSSize newSize = [[self cell] cellSizeForBounds:oldFrame]; NSRect newFrame = NSMakeRect(oldFrame.origin.x, oldFrame.origin.y, NSWidth(oldFrame), newSize.height); [self setFrame:newFrame]; @@ -180,9 +174,6 @@ NSString *const kDefaultServerType = @"google"; // Returns YES if we should send the report without asking the user first. - (BOOL)shouldSubmitSilently; -// Returns YES if the minidump was generated on demand. -- (BOOL)isOnDemand; - // Returns YES if we should ask the user to provide comments. - (BOOL)shouldRequestComments; @@ -196,11 +187,11 @@ NSString *const kDefaultServerType = @"google"; // Returns the short description of the crash, suitable for use as a dialog // title (e.g., "The application Foo has quit unexpectedly"). -- (NSString*)shortDialogMessage; +- (NSString*)shortCrashDialogMessage; // Return explanatory text about the crash and the reporter, suitable for the // body text of a dialog. -- (NSString*)explanatoryDialogText; +- (NSString*)explanatoryCrashDialogText; // Returns the amount of time the UI should be shown before timing out. - (NSTimeInterval)messageTimeout; @@ -216,7 +207,7 @@ NSString *const kDefaultServerType = @"google"; - (void)removeEmailPrompt; // Run an alert window with the given timeout. Returns -// NSRunStoppedResponse if the timeout is exceeded. A timeout of 0 +// NSAlertButtonDefault if the timeout is exceeded. A timeout of 0 // queues the message immediately in the modal run loop. - (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout; @@ -244,16 +235,6 @@ NSString *const kDefaultServerType = @"google"; // will be uploaded to the crash server. - (void)addServerParameter:(id)value forKey:(NSString *)key; -// This method is used to periodically update the UI with how many -// seconds are left in the dialog display. -- (void)updateSecondsLeftInDialogDisplay:(NSTimer*)theTimer; - -// When we receive this notification, it means that the user has -// begun editing the email address or comments field, and we disable -// the timers so that the user has as long as they want to type -// in their comments/email. -- (void)controlTextDidBeginEditing:(NSNotification *)aNotification; - @end @implementation Reporter @@ -280,7 +261,6 @@ NSString *const kDefaultServerType = @"google"; - (id)initWithConfigurationFD:(int)fd { if ((self = [super init])) { configFile_ = fd; - remainingDialogTime_ = 0; } // Because the reporter is embedded in the framework (and many copies @@ -565,8 +545,8 @@ NSString *const kDefaultServerType = @"google"; } } else { // Create an alert panel to tell the user something happened - NSPanel* alert = NSGetAlertPanel([self shortDialogMessage], - [self explanatoryDialogText], + NSPanel* alert = NSGetAlertPanel([self shortCrashDialogMessage], + [self explanatoryCrashDialogText], NSLocalizedString(@"sendReportButton", @""), NSLocalizedString(@"cancelButton", @""), nil); @@ -586,11 +566,11 @@ NSString *const kDefaultServerType = @"google"; // "fall" as text areas are shrunk from their overly-large IB sizes. // Localize the header. No resizing needed, as it has plenty of room. - [dialogTitle_ setStringValue:[self shortDialogMessage]]; + [dialogTitle_ setStringValue:[self shortCrashDialogMessage]]; // Localize the explanatory text field. [commentMessage_ setStringValue:[NSString stringWithFormat:@"%@\n\n%@", - [self explanatoryDialogText], + [self explanatoryCrashDialogText], NSLocalizedString(@"commentsMsg", @"")]]; float commentHeightDelta = [commentMessage_ breakpad_adjustHeightToFit]; [headerBox_ breakpad_shiftVertically:commentHeightDelta]; @@ -635,13 +615,9 @@ NSString *const kDefaultServerType = @"google"; - (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout { // Queue a |stopModal| message to be performed in |timeout| seconds. if (timeout > 0.001) { - remainingDialogTime_ = timeout; - SEL updateSelector = @selector(updateSecondsLeftInDialogDisplay:); - messageTimer_ = [NSTimer scheduledTimerWithTimeInterval:1.0 - target:self - selector:updateSelector - userInfo:nil - repeats:YES]; + [NSApp performSelector:@selector(stopModal) + withObject:nil + afterDelay:timeout]; } // Run the window modally and wait for either a |stopModal| message or a @@ -649,6 +625,12 @@ NSString *const kDefaultServerType = @"google"; [NSApp activateIgnoringOtherApps:YES]; int returnMethod = [NSApp runModalForWindow:window]; + // Cancel the pending |stopModal| message. + if (returnMethod != NSRunStoppedResponse) { + [NSObject cancelPreviousPerformRequestsWithTarget:NSApp + selector:@selector(stopModal) + object:nil]; + } return returnMethod; } @@ -685,10 +667,8 @@ NSString *const kDefaultServerType = @"google"; textView:(NSTextView*)textView doCommandBySelector:(SEL)commandSelector { BOOL result = NO; - // If the user has entered text on the comment field, don't end - // editing on "return". - if (control == commentsEntryField_ && - commandSelector == @selector(insertNewline:) + // If the user has entered text, don't end editing on "return" + if (commandSelector == @selector(insertNewline:) && [[textView string] length] > 0) { [textView insertNewlineIgnoringFieldEditor:self]; result = YES; @@ -696,50 +676,6 @@ doCommandBySelector:(SEL)commandSelector { return result; } -- (void)controlTextDidBeginEditing:(NSNotification *)aNotification { - [messageTimer_ invalidate]; - [self setCountdownMessage:@""]; -} - -- (void)updateSecondsLeftInDialogDisplay:(NSTimer*)theTimer { - remainingDialogTime_ -= 1; - - NSString *countdownMessage; - NSString *formatString; - - int displayedTimeLeft; // This can be either minutes or seconds. - - if (remainingDialogTime_ > 59) { - // calculate minutes remaining for UI purposes - displayedTimeLeft = (remainingDialogTime_ / 60); - - if (displayedTimeLeft == 1) { - formatString = NSLocalizedString(@"countdownMsgMinuteSingular", @""); - } else { - formatString = NSLocalizedString(@"countdownMsgMinutesPlural", @""); - } - } else { - displayedTimeLeft = remainingDialogTime_; - if (remainingDialogTime_ == 1) { - formatString = NSLocalizedString(@"countdownMsgSecondSingular", @""); - } else { - formatString = NSLocalizedString(@"countdownMsgSecondsPlural", @""); - } - } - countdownMessage = [NSString stringWithFormat:formatString, - displayedTimeLeft]; - if (remainingDialogTime_ <= 30) { - [countdownLabel_ setTextColor:[NSColor redColor]]; - } - [self setCountdownMessage:countdownMessage]; - if (remainingDialogTime_ <= 0) { - [messageTimer_ invalidate]; - [NSApp stopModal]; - } -} - - - #pragma mark Accessors #pragma mark - //============================================================================= @@ -766,17 +702,6 @@ doCommandBySelector:(SEL)commandSelector { } } -- (NSString *)countdownMessage { - return [[countdownMessage_ retain] autorelease]; -} - -- (void)setCountdownMessage:(NSString *)value { - if (countdownMessage_ != value) { - [countdownMessage_ release]; - countdownMessage_ = [value copy]; - } -} - #pragma mark - //============================================================================= - (BOOL)reportIntervalElapsed { @@ -805,11 +730,6 @@ doCommandBySelector:(SEL)commandSelector { return YES; } -- (BOOL)isOnDemand { - return [[parameters_ objectForKey:@BREAKPAD_ON_DEMAND] - isEqualToString:@"YES"]; -} - - (BOOL)shouldSubmitSilently { return [[parameters_ objectForKey:@BREAKPAD_SKIP_CONFIRM] isEqualToString:@"YES"]; @@ -825,40 +745,21 @@ doCommandBySelector:(SEL)commandSelector { isEqualToString:@"YES"]; } -- (NSString*)shortDialogMessage { +- (NSString*)shortCrashDialogMessage { NSString *displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; if (![displayName length]) displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT]; - if ([self isOnDemand]) { - return [NSString - stringWithFormat:NSLocalizedString(@"noCrashDialogHeader", @""), - displayName]; - } else { - return [NSString - stringWithFormat:NSLocalizedString(@"crashDialogHeader", @""), - displayName]; - } + return [NSString stringWithFormat:NSLocalizedString(@"headerFmt", @""), + displayName]; } -- (NSString*)explanatoryDialogText { - NSString *displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; - if (![displayName length]) - displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT]; - +- (NSString*)explanatoryCrashDialogText { NSString *vendor = [parameters_ objectForKey:@BREAKPAD_VENDOR]; if (![vendor length]) vendor = @"unknown vendor"; - if ([self isOnDemand]) { - return [NSString - stringWithFormat:NSLocalizedString(@"noCrashDialogMsg", @""), - vendor, displayName]; - } else { - return [NSString - stringWithFormat:NSLocalizedString(@"crashDialogMsg", @""), - vendor]; - } + return [NSString stringWithFormat:NSLocalizedString(@"msgFmt", @""), vendor]; } - (NSTimeInterval)messageTimeout { @@ -1039,77 +940,6 @@ doCommandBySelector:(SEL)commandSelector { [super dealloc]; } -- (void)awakeFromNib { - [emailEntryField_ setMaximumLength:kEmailMaxLength]; - [commentsEntryField_ setMaximumLength:kUserCommentsMaxLength]; -} - -@end - -//============================================================================= -@implementation LengthLimitingTextField - -- (void) setMaximumLength:(unsigned int)maxLength { - maximumLength_ = maxLength; -} - -// This is the method we're overriding in NSTextField, which lets us -// limit the user's input if it makes the string too long. -- (BOOL) textView:(NSTextView *)textView -shouldChangeTextInRange:(NSRange)affectedCharRange - replacementString:(NSString *)replacementString { - - // Sometimes the range comes in invalid, so reject if we can't - // figure out if the replacement text is too long. - if (affectedCharRange.location == NSNotFound) { - return NO; - } - // Figure out what the new string length would be, taking into - // account user selections. - int newStringLength = - [[textView string] length] - affectedCharRange.length + - [replacementString length]; - if (newStringLength > maximumLength_) { - return NO; - } else { - return YES; - } -} - -// Cut, copy, and paste have to be caught specifically since there is no menu. -- (BOOL)performKeyEquivalent:(NSEvent*)event { - // Only handle the key equivalent if |self| is the text field with focus. - NSText* fieldEditor = [self currentEditor]; - if (fieldEditor != nil) { - // Check for a single "Command" modifier - unsigned int modifiers = [event modifierFlags]; - modifiers &= NSDeviceIndependentModifierFlagsMask; - if (modifiers == NSCommandKeyMask) { - // Now, check for Select All, Cut, Copy, or Paste key equivalents. - NSString* characters = [event characters]; - // Select All is Command-A. - if ([characters isEqualToString:@"a"]) { - [fieldEditor selectAll:self]; - return YES; - // Cut is Command-X. - } else if ([characters isEqualToString:@"x"]) { - [fieldEditor cut:self]; - return YES; - // Copy is Command-C. - } else if ([characters isEqualToString:@"c"]) { - [fieldEditor copy:self]; - return YES; - // Paste is Command-V. - } else if ([characters isEqualToString:@"v"]) { - [fieldEditor paste:self]; - return YES; - } - } - } - // Let the super class handle the rest (e.g. Command-Period will cancel). - return [super performKeyEquivalent:event]; -} - @end //============================================================================= diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/sender/goArrow.png b/toolkit/crashreporter/google-breakpad/src/client/mac/sender/goArrow.png deleted file mode 100644 index f318a56711d43d1b8925f1c26e709da0872dd607..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3591 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 #include -#include "common/linux/linux_syscall_support.h" -#include "common/linux/linux_libc_support.h" #include "client/minidump_file_writer-inl.h" #include "common/string_conversion.h" @@ -55,11 +53,7 @@ MinidumpFileWriter::~MinidumpFileWriter() { bool MinidumpFileWriter::Open(const char *path) { assert(file_ == -1); -#if __linux__ - file_ = sys_open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); -#else file_ = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); -#endif return file_ != -1; } @@ -69,11 +63,7 @@ bool MinidumpFileWriter::Close() { if (file_ != -1) { ftruncate(file_, position_); -#if __linux__ - result = (sys_close(file_) == 0); -#else result = (close(file_) == 0); -#endif file_ = -1; } @@ -237,16 +227,9 @@ bool MinidumpFileWriter::Copy(MDRVA position, const void *src, ssize_t size) { return false; // Seek and write the data -#if __linux__ - if (sys_lseek(file_, position, SEEK_SET) == static_cast(position)) { - if (sys_write(file_, src, size) == size) { -#else - if (lseek(file_, position, SEEK_SET) == static_cast(position)) { - if (write(file_, src, size) == size) { -#endif + if (lseek(file_, position, SEEK_SET) == static_cast(position)) + if (write(file_, src, size) == size) return true; - } - } return false; } diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/breakpad_client.sln b/toolkit/crashreporter/google-breakpad/src/client/windows/breakpad_client.sln index d938a374c92..8c9c02b2fe1 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/windows/breakpad_client.sln +++ b/toolkit/crashreporter/google-breakpad/src/client/windows/breakpad_client.sln @@ -10,8 +10,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "crash_report_sender", "send EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "crash_generation", "crash_generation\crash_generation.vcproj", "{A820AF62-6239-4693-8430-4F516C1838F4}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "exception_handler_test", "handler\exception_handler_test\exception_handler_test.vcproj", "{89094A11-CF25-4037-AF43-EACFA751405E}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -44,14 +42,6 @@ Global {A820AF62-6239-4693-8430-4F516C1838F4}.Release|Win32.Build.0 = Release|Win32 {A820AF62-6239-4693-8430-4F516C1838F4}.ReleaseStaticCRT|Win32.ActiveCfg = ReleaseStaticCRT|Win32 {A820AF62-6239-4693-8430-4F516C1838F4}.ReleaseStaticCRT|Win32.Build.0 = ReleaseStaticCRT|Win32 - {89094A11-CF25-4037-AF43-EACFA751405E}.Debug|Win32.ActiveCfg = Debug|Win32 - {89094A11-CF25-4037-AF43-EACFA751405E}.Debug|Win32.Build.0 = Debug|Win32 - {89094A11-CF25-4037-AF43-EACFA751405E}.DebugStaticCRT|Win32.ActiveCfg = Debug|Win32 - {89094A11-CF25-4037-AF43-EACFA751405E}.DebugStaticCRT|Win32.Build.0 = Debug|Win32 - {89094A11-CF25-4037-AF43-EACFA751405E}.Release|Win32.ActiveCfg = Release|Win32 - {89094A11-CF25-4037-AF43-EACFA751405E}.Release|Win32.Build.0 = Release|Win32 - {89094A11-CF25-4037-AF43-EACFA751405E}.ReleaseStaticCRT|Win32.ActiveCfg = Release|Win32 - {89094A11-CF25-4037-AF43-EACFA751405E}.ReleaseStaticCRT|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.cc b/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.cc index dafa5c2acb1..ac76e590f5f 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.cc +++ b/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.cc @@ -218,14 +218,12 @@ bool CrashGenerationServer::Start() { } // Register a callback with the thread pool for the client connection. - if (!RegisterWaitForSingleObject(&pipe_wait_handle_, - overlapped_.hEvent, - OnPipeConnected, - this, - INFINITE, - kPipeIOThreadFlags)) { - return false; - } + RegisterWaitForSingleObject(&pipe_wait_handle_, + overlapped_.hEvent, + OnPipeConnected, + this, + INFINITE, + kPipeIOThreadFlags); pipe_ = CreateNamedPipe(pipe_name_.c_str(), kPipeAttr, diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc index be9e5eb21ad..48900273d39 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc +++ b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc @@ -249,12 +249,12 @@ ExceptionHandler::~ExceptionHandler() { // TODO(mmentovai): use advapi32!ReportEvent to log the warning to the // system's application event log. fprintf(stderr, "warning: removing Breakpad handler out of order\n"); - vector::iterator iterator = handler_stack_->begin(); - while (iterator != handler_stack_->end()) { + for (vector::iterator iterator = + handler_stack_->begin(); + iterator != handler_stack_->end(); + ++iterator) { if (*iterator == this) { - iterator = handler_stack_->erase(iterator); - } else { - ++iterator; + handler_stack_->erase(iterator); } } } diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.cc b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.cc deleted file mode 100644 index 2b7377ea578..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.cc +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2009, 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 "breakpad_googletest_includes.h" -#include "client/windows/crash_generation/crash_generation_server.h" -#include "client/windows/handler/exception_handler.h" -#include -#include -#include -#include -#include - -namespace { -const wchar_t kPipeName[] = L"\\\\.\\pipe\\BreakpadCrashTest\\TestCaseServer"; -const char kSuccessIndicator[] = "success"; -const char kFailureIndicator[] = "failure"; - -// Utility function to test for a path's existence. -BOOL DoesPathExist(const TCHAR *path_name); - -class ExceptionHandlerDeathTest : public ::testing::Test { -protected: - // Member variable for each test that they can use - // for temporary storage. - TCHAR temp_path_[MAX_PATH]; - // Actually constructs a temp path name. - virtual void SetUp(); - // A helper method that tests can use to crash. - void DoCrash(); -}; - -void ExceptionHandlerDeathTest::SetUp() { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - TCHAR temp_path[MAX_PATH] = { '\0' }; - TCHAR test_name_wide[MAX_PATH] = { '\0' }; - // We want the temporary directory to be what the OS returns - // to us, + the test case name. - GetTempPath(MAX_PATH, temp_path); - // THe test case name is exposed to use as a c-style string, - // But we might be working in UNICODE here on Windows. - int dwRet = MultiByteToWideChar(CP_ACP, 0, test_info->name(), - (int)strlen(test_info->name()), test_name_wide, MAX_PATH); - if (!dwRet) { - assert(false); - } - StringCchPrintfW(temp_path_, MAX_PATH, L"%s%s", temp_path, test_name_wide); - CreateDirectory(temp_path_, NULL); -} - -BOOL DoesPathExist(const TCHAR *path_name) { - DWORD flags = GetFileAttributes(path_name); - if (flags == INVALID_FILE_ATTRIBUTES) { - return FALSE; - } - return TRUE; -} - -bool MinidumpWrittenCallback(const wchar_t* dump_path, - const wchar_t* minidump_id, - void* context, - EXCEPTION_POINTERS* exinfo, - MDRawAssertionInfo* assertion, - bool succeeded) { - if (succeeded && DoesPathExist(dump_path)) { - fprintf(stderr, kSuccessIndicator); - } else { - fprintf(stderr, kFailureIndicator); - } - // If we don't flush, the output doesn't get sent before - // this process dies. - fflush(stderr); - return succeeded; -} - -TEST_F(ExceptionHandlerDeathTest, InProcTest) { - // For the in-proc test, we just need to instantiate an exception - // handler in in-proc mode, and crash. Since the entire test is - // reexecuted in the child process, we don't have to worry about - // the semantics of the exception handler being inherited/not - // inherited across CreateProcess(). - ASSERT_TRUE(DoesPathExist(temp_path_)); - google_breakpad::ExceptionHandler *exc = - new google_breakpad::ExceptionHandler( - temp_path_, NULL, &MinidumpWrittenCallback, NULL, - google_breakpad::ExceptionHandler::HANDLER_ALL); - int *i = NULL; - ASSERT_DEATH((*i)++, kSuccessIndicator); - delete exc; -} - -static bool gDumpCallbackCalled = false; - -void clientDumpCallback(void *dump_context, - const google_breakpad::ClientInfo *client_info, - const std::wstring *dump_path){ - - gDumpCallbackCalled = true; -} - -void ExceptionHandlerDeathTest::DoCrash() { - google_breakpad::ExceptionHandler *exc = - new google_breakpad::ExceptionHandler( - temp_path_, NULL, NULL, NULL, - google_breakpad::ExceptionHandler::HANDLER_ALL, MiniDumpNormal, kPipeName, - NULL); - // Although this is executing in the child process of the death test, - // if it's not true we'll still get an error rather than the crash - // being expected. - ASSERT_TRUE(exc->IsOutOfProcess()); - int *i = NULL; - printf("%d\n", (*i)++); -} - -TEST_F(ExceptionHandlerDeathTest, OutOfProcTest) { - // We can take advantage of a detail of google test here to save some - // complexity in testing: when you do a death test, it actually forks. - // So we can make the main test harness the crash generation server, - // and call ASSERT_DEATH on a NULL dereference, it to expecting test - // the out of process scenario, since it's happening in a different - // process! This is different from the above because, above, we pass - // a NULL pipe name, and we also don't start a crash generation server. - - ASSERT_TRUE(DoesPathExist(temp_path_)); - std::wstring dump_path(temp_path_); - google_breakpad::CrashGenerationServer server( - kPipeName, NULL, NULL, NULL, &clientDumpCallback, NULL, NULL, NULL, true, - &dump_path); - - // This HAS to be EXPECT_, because when this test case is executed in the - // child process, the server registration will fail due to the named pipe - // being the same. - EXPECT_TRUE(server.Start()); - EXPECT_FALSE(gDumpCallbackCalled); - ASSERT_DEATH(this->DoCrash(), ""); - EXPECT_TRUE(gDumpCallbackCalled); -} -} \ No newline at end of file diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.vcproj b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.vcproj deleted file mode 100644 index 4708d88e94f..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.vcproj +++ /dev/null @@ -1,266 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.cc b/toolkit/crashreporter/google-breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.cc index 5dfe4051027..a3c560bd5d0 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.cc +++ b/toolkit/crashreporter/google-breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.cc @@ -183,7 +183,7 @@ bool ShowDumpResults(const wchar_t* dump_path, delete [] text; } - QueueUserWorkItem(AppendTextWorker, text, WT_EXECUTEDEFAULT); + AppendTextWorker(text); return succeeded; } @@ -467,7 +467,6 @@ int APIENTRY _tWinMain(HINSTANCE instance, CustomClientInfo custom_info = {kCustomInfoEntries, kCustomInfoCount}; - CrashServerStart(); // This is needed for CRT to not show dialog for invalid param // failures and instead let the code handle it. _CrtSetReportMode(_CRT_ASSERT, 0); diff --git a/toolkit/crashreporter/google-breakpad/src/common/Makefile.in b/toolkit/crashreporter/google-breakpad/src/common/Makefile.in index 78fd6fd5964..bdfe718b431 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/Makefile.in +++ b/toolkit/crashreporter/google-breakpad/src/common/Makefile.in @@ -65,3 +65,9 @@ FORCE_STATIC_LIB = 1 FORCE_USE_PIC = 1 include $(topsrcdir)/config/rules.mk + +# XXX, bug 417045, make -jN combines badly with -save-temps in +# CFLAGS/CXXFLAGS (for stabs symbols with XCode3) +ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) +.NOTPARALLEL: +endif diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/Makefile.in b/toolkit/crashreporter/google-breakpad/src/common/linux/Makefile.in index bb87b352595..942e1ada45c 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/Makefile.in +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/Makefile.in @@ -47,18 +47,18 @@ HOST_LIBRARY_NAME = host_breakpad_linux_common_s LOCAL_INCLUDES = -I$(srcdir)/../.. +# not compiling http_upload.cc currently +# since it depends on libcurl CPPSRCS = \ + dump_symbols.cc \ file_id.cc \ guid_creator.cc \ - http_upload.cc \ $(NULL) HOST_CPPSRCS = \ dump_symbols.cc \ file_id.cc \ guid_creator.cc \ - module.cc \ - stabs_reader.cc \ $(NULL) # need static lib diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc index a78881899a4..046c7b346b5 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc @@ -27,48 +27,114 @@ // (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 +#include +#include +#include +#include #include #include #include #include #include -#include #include +#include #include #include #include - #include -#include -#include -#include -#include + #include #include -#include -#include #include +#include #include "common/linux/dump_symbols.h" #include "common/linux/file_id.h" -#include "common/linux/module.h" -#include "common/linux/stabs_reader.h" +#include "common/linux/guid_creator.h" +#include "processor/scoped_ptr.h" // This namespace contains helper functions. namespace { -using google_breakpad::Module; -using std::vector; +// Infomation of a line. +struct LineInfo { + // The index into string table for the name of the source file which + // this line belongs to. + // Load from stab symbol. + uint32_t source_name_index; + // Offset from start of the function. + // Load from stab symbol. + ElfW(Off) rva_to_func; + // Offset from base of the loading binary. + ElfW(Off) rva_to_base; + // Size of the line. + // It is the difference of the starting address of the line and starting + // address of the next N_SLINE, N_FUN or N_SO. + uint32_t size; + // Line number. + uint32_t line_num; + // Id of the source file for this line. + int source_id; +}; + +typedef std::list LineInfoList; + +// Information of a function. +struct FuncInfo { + // Name of the function. + const char *name; + // Offset from the base of the loading address. + ElfW(Off) rva_to_base; + // Virtual address of the function. + // Load from stab symbol. + ElfW(Addr) addr; + // Size of the function. + // It is the difference of the starting address of the function and starting + // address of the next N_FUN or N_SO. + uint32_t size; + // Total size of stack parameters. + uint32_t stack_param_size; + // Is there any lines included from other files? + bool has_sol; + // Line information array. + LineInfoList line_info; +}; + +typedef std::list FuncInfoList; + +// Information of a source file. +struct SourceFileInfo { + // Name string index into the string table. + uint32_t name_index; + // Name of the source file. + const char *name; + // Starting address of the source file. + ElfW(Addr) addr; + // Id of the source file. + int source_id; + // Functions information. + FuncInfoList func_info; +}; + +typedef std::list SourceFileInfoList; + +// Information of a symbol table. +// This is the root of all types of symbol. +struct SymbolInfo { + SourceFileInfoList source_file_info; + + // The next source id for newly found source file. + int next_source_id; +}; // Stab section name. static const char *kStabName = ".stab"; // Demangle using abi call. // Older GCC may not support it. -static std::string Demangle(const std::string &mangled) { +static std::string Demangle(const char *mangled) { int status = 0; - char *demangled = abi::__cxa_demangle(mangled.c_str(), NULL, NULL, &status); + char *demangled = abi::__cxa_demangle(mangled, NULL, NULL, &status); if (status == 0 && demangled != NULL) { std::string str(demangled); free(demangled); @@ -80,7 +146,7 @@ static std::string Demangle(const std::string &mangled) { // Fix offset into virtual address by adding the mapped base into offsets. // Make life easier when want to find something by offset. static void FixAddress(void *obj_base) { - ElfW(Addr) base = reinterpret_cast(obj_base); + ElfW(Word) base = reinterpret_cast(obj_base); ElfW(Ehdr) *elf_header = static_cast(obj_base); elf_header->e_phoff += base; elf_header->e_shoff += base; @@ -121,219 +187,384 @@ static const ElfW(Shdr) *FindSectionByName(const char *name, for (int i = 0; i < nsection; ++i) { const char *section_name = - reinterpret_cast(strtab->sh_offset + sections[i].sh_name); + (char*)(strtab->sh_offset + sections[i].sh_name); if (!strncmp(name, section_name, name_len)) return sections + i; } return NULL; } -// Our handler class for STABS data. -class DumpStabsHandler: public google_breakpad::StabsHandler { - public: - DumpStabsHandler(Module *module) : - module_(module), - comp_unit_base_address_(0), - current_function_(NULL), - current_source_file_(NULL), - current_source_file_name_(NULL) { } - - bool StartCompilationUnit(const char *name, uint64_t address, - const char *build_directory); - bool EndCompilationUnit(uint64_t address); - bool StartFunction(const std::string &name, uint64_t address); - bool EndFunction(uint64_t address); - bool Line(uint64_t address, const char *name, int number); - - // Do any final processing necessary to make module_ contain all the - // data provided by the STABS reader. - // - // Because STABS does not provide reliable size information for - // functions and lines, we need to make a pass over the data after - // processing all the STABS to compute those sizes. We take care of - // that here. - void Finalize(); - - private: - - // An arbitrary, but very large, size to use for functions whose - // size we can't compute properly. - static const uint64_t kFallbackSize = 0x10000000; - - // The module we're contributing debugging info to. - Module *module_; - - // The functions we've generated so far. We don't add these to - // module_ as we parse them. Instead, we wait until we've computed - // their ending address, and their lines' ending addresses. - // - // We could just stick them in module_ from the outset, but if - // module_ already contains data gathered from other debugging - // formats, that would complicate the size computation. - vector functions_; - - // Boundary addresses. STABS doesn't necessarily supply sizes for - // functions and lines, so we need to compute them ourselves by - // finding the next object. - vector boundaries_; - - // The base address of the current compilation unit. We use this to - // recognize functions we should omit from the symbol file. (If you - // know the details of why we omit these, please patch this - // comment.) - Module::Address comp_unit_base_address_; - - // The function we're currently contributing lines to. - Module::Function *current_function_; - - // The last Module::File we got a line number in. - Module::File *current_source_file_; - - // The pointer in the .stabstr section of the name that - // current_source_file_ is built from. This allows us to quickly - // recognize when the current line is in the same file as the - // previous one (which it usually is). - const char *current_source_file_name_; -}; - -bool DumpStabsHandler::StartCompilationUnit(const char *name, uint64_t address, - const char *build_directory) { - assert(! comp_unit_base_address_); - current_source_file_name_ = name; - current_source_file_ = module_->FindFile(name); - comp_unit_base_address_ = address; - boundaries_.push_back(static_cast(address)); - return true; -} - -bool DumpStabsHandler::EndCompilationUnit(uint64_t address) { - assert(comp_unit_base_address_); - comp_unit_base_address_ = 0; - current_source_file_ = NULL; - current_source_file_name_ = NULL; - if (address) - boundaries_.push_back(static_cast(address)); - return true; -} - -bool DumpStabsHandler::StartFunction(const std::string &name, - uint64_t address) { - assert(! current_function_); - Module::Function *f = new Module::Function; - f->name_ = Demangle(name); - f->address_ = address; - f->size_ = 0; // We compute this in DumpStabsHandler::Finalize(). - f->parameter_size_ = 0; // We don't provide this information. - current_function_ = f; - boundaries_.push_back(static_cast(address)); - return true; -} - -bool DumpStabsHandler::EndFunction(uint64_t address) { - assert(current_function_); - // Functions in this compilation unit should have address bigger - // than the compilation unit's starting address. There may be a lot - // of duplicated entries for functions in the STABS data; only one - // entry can meet this requirement. - // - // (I don't really understand the above comment; just bringing it - // along from the previous code, and leaving the behaivor unchanged. - // If you know the whole story, please patch this comment. --jimb) - if (current_function_->address_ >= comp_unit_base_address_) - functions_.push_back(current_function_); - else - delete current_function_; - current_function_ = NULL; - if (address) - boundaries_.push_back(static_cast(address)); - return true; -} - -bool DumpStabsHandler::Line(uint64_t address, const char *name, int number) { - assert(current_function_); - assert(current_source_file_); - if (name != current_source_file_name_) { - current_source_file_ = module_->FindFile(name); - current_source_file_name_ = name; +// TODO(liuli): Computer the stack parameter size. +// Expect parameter variables are immediately following the N_FUN symbol. +// Will need to parse the type information to get a correct size. +static int LoadStackParamSize(struct nlist *list, + struct nlist *list_end, + struct FuncInfo *func_info) { + struct nlist *cur_list = list; + assert(cur_list->n_type == N_FUN); + ++cur_list; + int step = 1; + while (cur_list < list_end && cur_list->n_type == N_PSYM) { + ++cur_list; + ++step; } - Module::Line line; - line.address_ = address; - line.size_ = 0; // We compute this in DumpStabsHandler::Finalize(). - line.file_ = current_source_file_; - line.number_ = number; - current_function_->lines_.push_back(line); - return true; + func_info->stack_param_size = 0; + return step; } -void DumpStabsHandler::Finalize() { - // Sort our boundary list, so we can search it quickly. - sort(boundaries_.begin(), boundaries_.end()); - // Sort all functions by address, just for neatness. - sort(functions_.begin(), functions_.end(), - Module::Function::CompareByAddress); - for (vector::iterator func_it = functions_.begin(); - func_it != functions_.end(); - func_it++) { - Module::Function *f = *func_it; - // Compute the function f's size. - vector::iterator boundary - = std::upper_bound(boundaries_.begin(), boundaries_.end(), f->address_); - if (boundary != boundaries_.end()) - f->size_ = *boundary - f->address_; - else - // If this is the last function in the module, and the STABS - // reader was unable to give us its ending address, then assign - // it a bogus, very large value. This will happen at most once - // per module: since we've added all functions' addresses to the - // boundary table, only one can be the last. - f->size_ = kFallbackSize; +static int LoadLineInfo(struct nlist *list, + struct nlist *list_end, + const struct SourceFileInfo &source_file_info, + struct FuncInfo *func_info) { + struct nlist *cur_list = list; + func_info->has_sol = false; + // Records which source file the following lines belongs. Default + // to the file we are handling. This helps us handling inlined source. + // When encountering N_SOL, we will change this to the source file + // specified by N_SOL. + int current_source_name_index = source_file_info.name_index; + do { + // Skip non line information. + while (cur_list < list_end && cur_list->n_type != N_SLINE) { + // Only exit when got another function, or source file. + if (cur_list->n_type == N_FUN || cur_list->n_type == N_SO) + return cur_list - list; + // N_SOL means source lines following it will be from + // another source file. + if (cur_list->n_type == N_SOL) { + func_info->has_sol = true; - // Compute sizes for each of the function f's lines --- if it has any. - if (! f->lines_.empty()) { - stable_sort(f->lines_.begin(), f->lines_.end(), - Module::Line::CompareByAddress); - vector::iterator last_line = f->lines_.end() - 1; - for (vector::iterator line_it = f->lines_.begin(); - line_it != last_line; line_it++) - line_it[0].size_ = line_it[1].address_ - line_it[0].address_; - // Compute the size of the last line from f's end address. - last_line->size_ = (f->address_ + f->size_) - last_line->address_; + if (cur_list->n_un.n_strx > 0 && + cur_list->n_un.n_strx != current_source_name_index) { + // The following lines will be from this source file. + current_source_name_index = cur_list->n_un.n_strx; + } + } + ++cur_list; + } + struct LineInfo line; + while (cur_list < list_end && cur_list->n_type == N_SLINE) { + line.source_name_index = current_source_name_index; + line.rva_to_func = cur_list->n_value; + // n_desc is a signed short + line.line_num = (unsigned short)cur_list->n_desc; + // Don't set it here. + // Will be processed in later pass. + line.source_id = -1; + func_info->line_info.push_back(line); + ++cur_list; + } + } while (list < list_end); + + return cur_list - list; +} + +static int LoadFuncSymbols(struct nlist *list, + struct nlist *list_end, + const ElfW(Shdr) *stabstr_section, + struct SourceFileInfo *source_file_info) { + struct nlist *cur_list = list; + assert(cur_list->n_type == N_SO); + ++cur_list; + source_file_info->func_info.clear(); + while (cur_list < list_end) { + // Go until the function symbol. + while (cur_list < list_end && cur_list->n_type != N_FUN) { + if (cur_list->n_type == N_SO) { + return cur_list - list; + } + ++cur_list; + continue; + } + if (cur_list->n_type == N_FUN) { + struct FuncInfo func_info; + func_info.name = + reinterpret_cast(cur_list->n_un.n_strx + + stabstr_section->sh_offset); + func_info.addr = cur_list->n_value; + func_info.rva_to_base = 0; + func_info.size = 0; + func_info.stack_param_size = 0; + func_info.has_sol = 0; + + // Stack parameter size. + cur_list += LoadStackParamSize(cur_list, list_end, &func_info); + // Line info. + cur_list += LoadLineInfo(cur_list, + list_end, + *source_file_info, + &func_info); + + // Functions in this module should have address bigger than the module + // startring address. + // There maybe a lot of duplicated entry for a function in the symbol, + // only one of them can met this. + if (func_info.addr >= source_file_info->addr) { + source_file_info->func_info.push_back(func_info); + } } } - // Now that everything has a size, add our functions to the module, and - // dispose of our private list. - module_->AddFunctions(functions_.begin(), functions_.end()); - functions_.clear(); + return cur_list - list; +} + +// Comapre the address. +// The argument should have a memeber named "addr" +template +static bool CompareAddress(T1 *a, T2 *b) { + return a->addr < b->addr; +} + +// Sort the array into increasing ordered array based on the virtual address. +// Return vector of pointers to the elements in the incoming array. So caller +// should make sure the returned vector lives longer than the incoming vector. +template +static std::vector SortByAddress( + Container *container) { + typedef typename Container::iterator It; + typedef typename Container::value_type T; + std::vector sorted_array_ptr; + sorted_array_ptr.reserve(container->size()); + for (It it = container->begin(); it != container->end(); it++) + sorted_array_ptr.push_back(&(*it)); + std::sort(sorted_array_ptr.begin(), + sorted_array_ptr.end(), + std::ptr_fun(CompareAddress)); + + return sorted_array_ptr; +} + +// Find the address of the next function or source file symbol in the symbol +// table. The address should be bigger than the current function's address. +static ElfW(Addr) NextAddress( + std::vector *sorted_functions, + std::vector *sorted_files, + const struct FuncInfo &func_info) { + std::vector::iterator next_func_iter = + std::find_if(sorted_functions->begin(), + sorted_functions->end(), + std::bind1st( + std::ptr_fun( + CompareAddress + ), + &func_info) + ); + if (next_func_iter != sorted_functions->end()) + return (*next_func_iter)->addr; + + std::vector::iterator next_file_iter = + std::find_if(sorted_files->begin(), + sorted_files->end(), + std::bind1st( + std::ptr_fun( + CompareAddress + ), + &func_info) + ); + if (next_file_iter != sorted_files->end()) { + return (*next_file_iter)->addr; + } + return 0; +} + +static int FindFileByNameIdx(uint32_t name_index, + SourceFileInfoList &files) { + for (SourceFileInfoList::iterator it = files.begin(); + it != files.end(); it++) { + if (it->name_index == name_index) + return it->source_id; + } + + return -1; +} + +// Add included file information. +// Also fix the source id for the line info. +static void AddIncludedFiles(struct SymbolInfo *symbols, + const ElfW(Shdr) *stabstr_section) { + for (SourceFileInfoList::iterator source_file_it = + symbols->source_file_info.begin(); + source_file_it != symbols->source_file_info.end(); + ++source_file_it) { + struct SourceFileInfo &source_file = *source_file_it; + + for (FuncInfoList::iterator func_info_it = source_file.func_info.begin(); + func_info_it != source_file.func_info.end(); + ++func_info_it) { + struct FuncInfo &func_info = *func_info_it; + + for (LineInfoList::iterator line_info_it = func_info.line_info.begin(); + line_info_it != func_info.line_info.end(); ++line_info_it) { + struct LineInfo &line_info = *line_info_it; + + assert(line_info.source_name_index > 0); + assert(source_file.name_index > 0); + + // Check if the line belongs to the source file by comparing the + // name index into string table. + if (line_info.source_name_index != source_file.name_index) { + // This line is not from the current source file, check if this + // source file has been added before. + int found_source_id = FindFileByNameIdx(line_info.source_name_index, + symbols->source_file_info); + if (found_source_id < 0) { + // Got a new included file. + // Those included files don't have address or line information. + SourceFileInfo new_file; + new_file.name_index = line_info.source_name_index; + new_file.name = reinterpret_cast(new_file.name_index + + stabstr_section->sh_offset); + new_file.addr = 0; + new_file.source_id = symbols->next_source_id++; + line_info.source_id = new_file.source_id; + symbols->source_file_info.push_back(new_file); + } else { + // The file has been added. + line_info.source_id = found_source_id; + } + } else { + // The line belongs to the file. + line_info.source_id = source_file.source_id; + } + } // for each line. + } // for each function. + } // for each source file. + +} + +// Compute size and rva information based on symbols loaded from stab section. +static bool ComputeSizeAndRVA(ElfW(Addr) loading_addr, + struct SymbolInfo *symbols) { + std::vector sorted_files = + SortByAddress(&(symbols->source_file_info)); + for (size_t i = 0; i < sorted_files.size(); ++i) { + struct SourceFileInfo &source_file = *sorted_files[i]; + std::vector sorted_functions = + SortByAddress(&(source_file.func_info)); + for (size_t j = 0; j < sorted_functions.size(); ++j) { + struct FuncInfo &func_info = *sorted_functions[j]; + assert(func_info.addr >= loading_addr); + func_info.rva_to_base = func_info.addr - loading_addr; + func_info.size = 0; + ElfW(Addr) next_addr = NextAddress(&sorted_functions, + &sorted_files, + func_info); + // I've noticed functions with an address bigger than any other functions + // and source files modules, this is probably the last function in the + // module, due to limitions of Linux stab symbol, it is impossible to get + // the exact size of this kind of function, thus we give it a default + // very big value. This should be safe since this is the last function. + // But it is a ugly hack..... + // The following code can reproduce the case: + // template + // void Foo(T value) { + // } + // + // int main(void) { + // Foo(10); + // Foo(std::string("hello")); + // return 0; + // } + // TODO(liuli): Find a better solution. + static const int kDefaultSize = 0x10000000; + static int no_next_addr_count = 0; + if (next_addr != 0) { + func_info.size = next_addr - func_info.addr; + } else { + if (no_next_addr_count > 1) { + fprintf(stderr, "Got more than one funtion without the \ + following symbol. Igore this function.\n"); + fprintf(stderr, "The dumped symbol may not correct.\n"); + assert(!"This should not happen!\n"); + func_info.size = 0; + continue; + } + + no_next_addr_count++; + func_info.size = kDefaultSize; + } + // Compute line size. + for (LineInfoList::iterator line_info_it = func_info.line_info.begin(); + line_info_it != func_info.line_info.end(); line_info_it++) { + struct LineInfo &line_info = *line_info_it; + LineInfoList::iterator next_line_info_it = line_info_it; + next_line_info_it++; + line_info.size = 0; + if (next_line_info_it != func_info.line_info.end()) { + line_info.size = + next_line_info_it->rva_to_func - line_info.rva_to_func; + } else { + // The last line in the function. + // If we can find a function or source file symbol immediately + // following the line, we can get the size of the line by computing + // the difference of the next address to the starting address of this + // line. + // Otherwise, we need to set a default big enough value. This occurs + // mostly because the this function is the last one in the module. + if (next_addr != 0) { + ElfW(Off) next_addr_offset = next_addr - func_info.addr; + line_info.size = next_addr_offset - line_info.rva_to_func; + } else { + line_info.size = kDefaultSize; + } + } + line_info.rva_to_base = line_info.rva_to_func + func_info.rva_to_base; + } // for each line. + } // for each function. + } // for each source file. + return true; } static bool LoadSymbols(const ElfW(Shdr) *stab_section, const ElfW(Shdr) *stabstr_section, - Module *module) { + ElfW(Addr) loading_addr, + struct SymbolInfo *symbols) { if (stab_section == NULL || stabstr_section == NULL) return false; - // A callback object to handle data from the STABS reader. - DumpStabsHandler handler(module); - // Find the addresses of the STABS data, and create a STABS reader object. - uint8_t *stabs = reinterpret_cast(stab_section->sh_offset); - uint8_t *stabstr = reinterpret_cast(stabstr_section->sh_offset); - google_breakpad::StabsReader reader(stabs, stab_section->sh_size, - stabstr, stabstr_section->sh_size, - &handler); - // Read the STABS data, and do post-processing. - if (! reader.Process()) - return false; - handler.Finalize(); - return true; + struct nlist *lists = + reinterpret_cast(stab_section->sh_offset); + int nstab = stab_section->sh_size / sizeof(struct nlist); + // First pass, load all symbols from the object file. + for (int i = 0; i < nstab; ) { + int step = 1; + struct nlist *cur_list = lists + i; + if (cur_list->n_type == N_SO) { + // FUNC
+ struct SourceFileInfo source_file_info; + source_file_info.name_index = cur_list->n_un.n_strx; + source_file_info.name = reinterpret_cast(cur_list->n_un.n_strx + + stabstr_section->sh_offset); + source_file_info.addr = cur_list->n_value; + if (strchr(source_file_info.name, '.')) + source_file_info.source_id = symbols->next_source_id++; + else + source_file_info.source_id = -1; + step = LoadFuncSymbols(cur_list, lists + nstab, + stabstr_section, &source_file_info); + symbols->source_file_info.push_back(source_file_info); + } + i += step; + } + + // Second pass, compute the size of functions and lines. + if (ComputeSizeAndRVA(loading_addr, symbols)) { + // Third pass, check for included source code, especially for header files. + // Until now, we only have compiling unit information, but they can + // have code from include files, add them here. + AddIncludedFiles(symbols, stabstr_section); + return true; + } + return false; } -static bool LoadSymbols(ElfW(Ehdr) *elf_header, Module *module) { +static bool LoadSymbols(ElfW(Ehdr) *elf_header, struct SymbolInfo *symbols) { // Translate all offsets in section headers into address. FixAddress(elf_header); ElfW(Addr) loading_addr = GetLoadingAddress( reinterpret_cast(elf_header->e_phoff), elf_header->e_phnum); - module->SetLoadAddress(loading_addr); const ElfW(Shdr) *sections = reinterpret_cast(elf_header->e_shoff); @@ -347,7 +578,107 @@ static bool LoadSymbols(ElfW(Ehdr) *elf_header, Module *module) { const ElfW(Shdr) *stabstr_section = stab_section->sh_link + sections; // Load symbols. - return LoadSymbols(stab_section, stabstr_section, module); + return LoadSymbols(stab_section, stabstr_section, loading_addr, symbols); +} + +static bool WriteModuleInfo(FILE *file, + ElfW(Half) arch, + const std::string &obj_file) { + const char *arch_name = NULL; + if (arch == EM_386) + arch_name = "x86"; + else if (arch == EM_X86_64) + arch_name = "x86_64"; + else + return false; + + unsigned char identifier[16]; + google_breakpad::FileID file_id(obj_file.c_str()); + if (file_id.ElfFileIdentifier(identifier)) { + char identifier_str[40]; + file_id.ConvertIdentifierToString(identifier, + identifier_str, sizeof(identifier_str)); + char id_no_dash[40]; + int id_no_dash_len = 0; + memset(id_no_dash, 0, sizeof(id_no_dash)); + for (int i = 0; identifier_str[i] != '\0'; ++i) + if (identifier_str[i] != '-') + id_no_dash[id_no_dash_len++] = identifier_str[i]; + // Add an extra "0" by the end. + id_no_dash[id_no_dash_len++] = '0'; + std::string filename = obj_file; + size_t slash_pos = obj_file.find_last_of("/"); + if (slash_pos != std::string::npos) + filename = obj_file.substr(slash_pos + 1); + return 0 <= fprintf(file, "MODULE Linux %s %s %s\n", arch_name, + id_no_dash, filename.c_str()); + } + return false; +} + +static bool WriteSourceFileInfo(FILE *file, const struct SymbolInfo &symbols) { + for (SourceFileInfoList::const_iterator it = + symbols.source_file_info.begin(); + it != symbols.source_file_info.end(); it++) { + if (it->source_id != -1) { + const char *name = it->name; + if (0 > fprintf(file, "FILE %d %s\n", it->source_id, name)) + return false; + } + } + return true; +} + +static bool WriteOneFunction(FILE *file, + const struct FuncInfo &func_info){ + // Discard the ending part of the name. + std::string func_name(func_info.name); + std::string::size_type last_colon = func_name.find_last_of(':'); + if (last_colon != std::string::npos) + func_name = func_name.substr(0, last_colon); + func_name = Demangle(func_name.c_str()); + + if (func_info.size <= 0) + return true; + + if (0 <= fprintf(file, "FUNC %lx %lx %d %s\n", + (unsigned long) func_info.rva_to_base, + (unsigned long) func_info.size, + func_info.stack_param_size, + func_name.c_str())) { + for (LineInfoList::const_iterator it = func_info.line_info.begin(); + it != func_info.line_info.end(); it++) { + const struct LineInfo &line_info = *it; + if (0 > fprintf(file, "%lx %lx %d %d\n", + (unsigned long) line_info.rva_to_base, + (unsigned long) line_info.size, + line_info.line_num, + line_info.source_id)) + return false; + } + return true; + } + return false; +} + +static bool WriteFunctionInfo(FILE *file, const struct SymbolInfo &symbols) { + for (SourceFileInfoList::const_iterator it = + symbols.source_file_info.begin(); + it != symbols.source_file_info.end(); it++) { + const struct SourceFileInfo &file_info = *it; + for (FuncInfoList::const_iterator fiIt = file_info.func_info.begin(); + fiIt != file_info.func_info.end(); fiIt++) { + const struct FuncInfo &func_info = *fiIt; + if (!WriteOneFunction(file, func_info)) + return false; + } + } + return true; +} + +static bool DumpStabSymbols(FILE *file, const struct SymbolInfo &symbols) { + return WriteSourceFileInfo(file, symbols) && + WriteFunctionInfo(file, symbols); } // @@ -402,48 +733,6 @@ class MmapWrapper { size_t size_; }; -// Return the breakpad symbol file identifier for the architecture of -// ELF_HEADER. -const char *ElfArchitecture(const ElfW(Ehdr) *elf_header) { - ElfW(Half) arch = elf_header->e_machine; - if (arch == EM_386) - return "x86"; - else if (arch == EM_X86_64) - return "x86_64"; - else - return NULL; -} - -// Format the Elf file identifier in IDENTIFIER as a UUID with the -// dashes removed. -std::string FormatIdentifier(unsigned char identifier[16]) { - char identifier_str[40]; - google_breakpad::FileID::ConvertIdentifierToString( - identifier, - identifier_str, - sizeof(identifier_str)); - std::string id_no_dash; - for (int i = 0; identifier_str[i] != '\0'; ++i) - if (identifier_str[i] != '-') - id_no_dash += identifier_str[i]; - // Add an extra "0" by the end. PDB files on Windows have an 'age' - // number appended to the end of the file identifier; this isn't - // really used or necessary on other platforms, but let's preserve - // the pattern. - id_no_dash += '0'; - return id_no_dash; -} - -// Return the non-directory portion of FILENAME: the portion after the -// last slash, or the whole filename if there are no slashes. -std::string BaseFileName(const std::string &filename) { - // Lots of copies! basename's behavior is less than ideal. - char *c_filename = strdup(filename.c_str()); - std::string base = basename(c_filename); - free(c_filename); - return base; -} - } // namespace namespace google_breakpad { @@ -465,27 +754,17 @@ bool DumpSymbols::WriteSymbolFile(const std::string &obj_file, ElfW(Ehdr) *elf_header = reinterpret_cast(obj_base); if (!IsValidElf(elf_header)) return false; + struct SymbolInfo symbols; + symbols.next_source_id = 0; - unsigned char identifier[16]; - google_breakpad::FileID file_id(obj_file.c_str()); - if (! file_id.ElfFileIdentifier(identifier)) - return false; + if (!LoadSymbols(elf_header, &symbols)) + return false; + // Write to symbol file. + if (WriteModuleInfo(sym_file, elf_header->e_machine, obj_file) && + DumpStabSymbols(sym_file, symbols)) + return true; - const char *architecture = ElfArchitecture(elf_header); - if (! architecture) - return false; - - std::string name = BaseFileName(obj_file); - std::string os = "Linux"; - std::string id = FormatIdentifier(identifier); - - Module module(name, os, architecture, id); - if (!LoadSymbols(elf_header, &module)) - return false; - if (!module.Write(sym_file)) - return false; - - return true; + return false; } } // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc index 34c9e508024..1adf2a1354a 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc @@ -32,74 +32,104 @@ // See file_id.h for documentation // -#include "common/linux/file_id.h" - -#include +#include +#include #include #include #include -#include #include +#include #include -#include -#include +#include "common/linux/file_id.h" +#include "common/md5.h" namespace google_breakpad { -FileID::FileID(const char* path) { +static bool FindElfTextSection(const void *elf_mapped_base, + const void **text_start, + int *text_size) { + assert(elf_mapped_base); + assert(text_start); + assert(text_size); + + const unsigned char *elf_base = + static_cast(elf_mapped_base); + const ElfW(Ehdr) *elf_header = + reinterpret_cast(elf_base); + if (memcmp(elf_header, ELFMAG, SELFMAG) != 0) + return false; + *text_start = NULL; + *text_size = 0; + const ElfW(Shdr) *sections = + reinterpret_cast(elf_base + elf_header->e_shoff); + const char *text_section_name = ".text"; + int name_len = strlen(text_section_name); + const ElfW(Shdr) *string_section = sections + elf_header->e_shstrndx; + const ElfW(Shdr) *text_section = NULL; + for (int i = 0; i < elf_header->e_shnum; ++i) { + if (sections[i].sh_type == SHT_PROGBITS) { + const char *section_name = (char*)(elf_base + + string_section->sh_offset + + sections[i].sh_name); + if (!strncmp(section_name, text_section_name, name_len)) { + text_section = §ions[i]; + break; + } + } + } + if (text_section != NULL && text_section->sh_size > 0) { + int text_section_size = text_section->sh_size; + *text_start = elf_base + text_section->sh_offset; + *text_size = text_section_size; + } + return true; +} + +FileID::FileID(const char *path) { strncpy(path_, path, sizeof(path_)); } -bool FileID::ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]) { - const ssize_t mapped_len = 4096; // Page size (matches WriteMappings()) +bool FileID::ElfFileIdentifier(unsigned char identifier[16]) { int fd = open(path_, O_RDONLY); if (fd < 0) return false; struct stat st; - if (fstat(fd, &st) != 0 || st.st_size <= mapped_len) { + if (fstat(fd, &st) != 0 && st.st_size <= 0) { close(fd); return false; } - void* base = mmap(NULL, mapped_len, + void *base = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - close(fd); - if (base == MAP_FAILED) + if (base == MAP_FAILED) { + close(fd); return false; - - memset(identifier, 0, kMDGUIDSize); - uint8_t* ptr = reinterpret_cast(base); - uint8_t* ptr_end = ptr + mapped_len; - while (ptr < ptr_end) { - for (unsigned i = 0; i < kMDGUIDSize; i++) - identifier[i] ^= ptr[i]; - ptr += kMDGUIDSize; + } + bool success = false; + const void *text_section = NULL; + int text_size = 0; + if (FindElfTextSection(base, &text_section, &text_size) && (text_size > 0)) { + struct MD5Context md5; + MD5Init(&md5); + MD5Update(&md5, + static_cast(text_section), + text_size); + MD5Final(identifier, &md5); + success = true; } - munmap(base, mapped_len); - return true; + close(fd); + munmap(base, st.st_size); + return success; } // static -void FileID::ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize], - char* buffer, int buffer_length) { - uint8_t identifier_swapped[kMDGUIDSize]; - - // Endian-ness swap to match dump processor expectation. - memcpy(identifier_swapped, identifier, kMDGUIDSize); - uint32_t* data1 = reinterpret_cast(identifier_swapped); - *data1 = htonl(*data1); - uint16_t* data2 = reinterpret_cast(identifier_swapped + 4); - *data2 = htons(*data2); - uint16_t* data3 = reinterpret_cast(identifier_swapped + 6); - *data3 = htons(*data3); - +void FileID::ConvertIdentifierToString(const unsigned char identifier[16], + char *buffer, int buffer_length) { int buffer_idx = 0; - for (unsigned int idx = 0; - (buffer_idx < buffer_length) && (idx < kMDGUIDSize); - ++idx) { - int hi = (identifier_swapped[idx] >> 4) & 0x0F; - int lo = (identifier_swapped[idx]) & 0x0F; + for (int idx = 0; (buffer_idx < buffer_length) && (idx < 16); ++idx) { + int hi = (identifier[idx] >> 4) & 0x0F; + int lo = (identifier[idx]) & 0x0F; if (idx == 4 || idx == 6 || idx == 8 || idx == 10) buffer[buffer_idx++] = '-'; diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.h b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.h index 31bb5e4a555..5e1cd6e1a00 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.h +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.h @@ -35,30 +35,25 @@ #include -#include "common/linux/guid_creator.h" - namespace google_breakpad { -static const size_t kMDGUIDSize = sizeof(MDGUID); - class FileID { public: - explicit FileID(const char* path); - ~FileID() {} + FileID(const char *path); + ~FileID() {}; // Load the identifier for the elf file path specified in the constructor into // |identifier|. Return false if the identifier could not be created for the // file. - // The current implementation will XOR the first page of data to generate an - // identifier. - bool ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]); + // The current implementation will return the MD5 hash of the file's bytes. + bool ElfFileIdentifier(unsigned char identifier[16]); // Convert the |identifier| data to a NULL terminated string. The string will // be formatted as a UUID (e.g., 22F065BB-FC9C-49F7-80FE-26A7CEBD7BCE). // The |buffer| should be at least 37 bytes long to receive all of the data // and termination. Shorter buffers will contain truncated data. - static void ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize], - char* buffer, int buffer_length); + static void ConvertIdentifierToString(const unsigned char identifier[16], + char *buffer, int buffer_length); private: // Storage for the path specified @@ -68,3 +63,4 @@ class FileID { } // namespace google_breakpad #endif // COMMON_LINUX_FILE_ID_H__ + diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.cc deleted file mode 100644 index f47a8e5ecdd..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.cc +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright (c) 2009, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -#include "common/linux/google_crashdump_uploader.h" -#include "common/linux/libcurl_wrapper.h" -#include "third_party/linux/include/glog/logging.h" - -#include -#include -#include - -namespace google_breakpad { - -GoogleCrashdumpUploader::GoogleCrashdumpUploader(const std::string& product, - const std::string& version, - const std::string& guid, - const std::string& ptime, - const std::string& ctime, - const std::string& email, - const std::string& comments, - const std::string& minidump_pathname, - const std::string& crash_server, - const std::string& proxy_host, - const std::string& proxy_userpassword) { - LibcurlWrapper* http_layer = new LibcurlWrapper(); - Init(product, - version, - guid, - ptime, - ctime, - email, - comments, - minidump_pathname, - crash_server, - proxy_host, - proxy_userpassword, - http_layer); -} - -GoogleCrashdumpUploader::GoogleCrashdumpUploader(const std::string& product, - const std::string& version, - const std::string& guid, - const std::string& ptime, - const std::string& ctime, - const std::string& email, - const std::string& comments, - const std::string& minidump_pathname, - const std::string& crash_server, - const std::string& proxy_host, - const std::string& proxy_userpassword, - LibcurlWrapper* http_layer) { - Init(product, - version, - guid, - ptime, - ctime, - email, - comments, - minidump_pathname, - crash_server, - proxy_host, - proxy_userpassword, - http_layer); -} - -void GoogleCrashdumpUploader::Init(const std::string& product, - const std::string& version, - const std::string& guid, - const std::string& ptime, - const std::string& ctime, - const std::string& email, - const std::string& comments, - const std::string& minidump_pathname, - const std::string& crash_server, - const std::string& proxy_host, - const std::string& proxy_userpassword, - LibcurlWrapper* http_layer) { - product_ = product; - version_ = version; - guid_ = guid; - ptime_ = ptime; - ctime_ = ctime; - email_ = email; - comments_ = comments; - http_layer_ = http_layer; - - crash_server_ = crash_server; - proxy_host_ = proxy_host; - proxy_userpassword_ = proxy_userpassword; - minidump_pathname_ = minidump_pathname; - LOG(INFO) << "Uploader initializing"; - LOG(INFO) << "\tProduct: " << product_; - LOG(INFO) << "\tVersion: " << version_; - LOG(INFO) << "\tGUID: " << guid_; - if (!ptime_.empty()) { - LOG(INFO) << "\tProcess uptime: " << ptime_; - } - if (!ctime_.empty()) { - LOG(INFO) << "\tCumulative Process uptime: " << ctime_; - } - if (!email_.empty()) { - LOG(INFO) << "\tEmail: " << email_; - } - if (!comments_.empty()) { - LOG(INFO) << "\tComments: " << comments_; - } -} - -bool GoogleCrashdumpUploader::CheckRequiredParametersArePresent() { - std::string error_text; - if (product_.empty()) { - error_text.append("\nProduct name must be specified."); - } - - if (version_.empty()) { - error_text.append("\nProduct version must be specified."); - } - - if (guid_.empty()) { - error_text.append("\nClient ID must be specified."); - } - - if (minidump_pathname_.empty()) { - error_text.append("\nMinidump pathname must be specified."); - } - - if (!error_text.empty()) { - LOG(ERROR) << error_text; - return false; - } - return true; - -} - -bool GoogleCrashdumpUploader::Upload() { - bool ok = http_layer_->Init(); - if (!ok) { - LOG(WARNING) << "http layer init failed"; - return ok; - } - - if (!CheckRequiredParametersArePresent()) { - return false; - } - - struct stat st; - int err = stat(minidump_pathname_.c_str(), &st); - if (err) { - LOG(WARNING) << minidump_pathname_ << " could not be found: " << errno; - return false; - } - - parameters_["prod"] = product_; - parameters_["ver"] = version_; - parameters_["guid"] = guid_; - parameters_["ptime"] = ptime_; - parameters_["ctime"] = ctime_; - parameters_["email"] = email_; - parameters_["comments_"] = comments_; - if (!http_layer_->AddFile(minidump_pathname_, - "upload_file_minidump")) { - return false; - } - LOG(INFO) << "Sending request to " << crash_server_; - return http_layer_->SendRequest(crash_server_, - parameters_, - NULL); -} -} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.h b/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.h deleted file mode 100644 index 5cea17d995e..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.h +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2009, 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 -#include - -namespace google_breakpad { - -class LibcurlWrapper; - -class GoogleCrashdumpUploader { - public: - GoogleCrashdumpUploader(const std::string& product, - const std::string& version, - const std::string& guid, - const std::string& ptime, - const std::string& ctime, - const std::string& email, - const std::string& comments, - const std::string& minidump_pathname, - const std::string& crash_server, - const std::string& proxy_host, - const std::string& proxy_userpassword); - - GoogleCrashdumpUploader(const std::string& product, - const std::string& version, - const std::string& guid, - const std::string& ptime, - const std::string& ctime, - const std::string& email, - const std::string& comments, - const std::string& minidump_pathname, - const std::string& crash_server, - const std::string& proxy_host, - const std::string& proxy_userpassword, - LibcurlWrapper* http_layer); - - void Init(const std::string& product, - const std::string& version, - const std::string& guid, - const std::string& ptime, - const std::string& ctime, - const std::string& email, - const std::string& comments, - const std::string& minidump_pathname, - const std::string& crash_server, - const std::string& proxy_host, - const std::string& proxy_userpassword, - LibcurlWrapper* http_layer); - bool Upload(); - - private: - bool CheckRequiredParametersArePresent(); - - LibcurlWrapper* http_layer_; - std::string product_; - std::string version_; - std::string guid_; - std::string ptime_; - std::string ctime_; - std::string email_; - std::string comments_; - std::string minidump_pathname_; - - std::string crash_server_; - std::string proxy_host_; - std::string proxy_userpassword_; - - std::map parameters_; -}; -} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader_test.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader_test.cc deleted file mode 100644 index c65355c98e2..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader_test.cc +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (c) 2009, 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. - -// Unit test for crash dump uploader. - -#include "common/linux/google_crashdump_uploader.h" -#include "common/linux/libcurl_wrapper.h" -#include "breakpad_googletest_includes.h" - -namespace google_breakpad { - -using ::testing::Return; -using ::testing::_; - -class MockLibcurlWrapper : public LibcurlWrapper { - public: - MOCK_METHOD0(Init, bool()); - MOCK_METHOD2(SetProxy, bool(const std::string& proxy_host, - const std::string& proxy_userpwd)); - MOCK_METHOD2(AddFile, bool(const std::string& upload_file_path, - const std::string& basename)); - MOCK_METHOD3(SendRequest, - bool(const std::string& url, - const std::map& parameters, - std::string* server_response)); -}; - -class GoogleCrashdumpUploaderTest : public ::testing::Test { -}; - -TEST_F(GoogleCrashdumpUploaderTest, InitFailsCausesUploadFailure) { - MockLibcurlWrapper m; - EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(false)); - GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar", - "1.0", - "AAA-BBB", - "", - "", - "test@test.com", - "none", - "/tmp/foo.dmp", - "http://foo.com", - "", - "", - &m); - ASSERT_FALSE(uploader->Upload()); -} - -TEST_F(GoogleCrashdumpUploaderTest, TestSendRequestHappensWithValidParameters) { - // Create a temp file - char tempfn[80] = "/tmp/googletest-upload-XXXXXX"; - int fd = mkstemp(tempfn); - ASSERT_NE(fd, -1); - close(fd); - - MockLibcurlWrapper m; - EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true)); - EXPECT_CALL(m, AddFile(tempfn, _)).WillOnce(Return(true)); - EXPECT_CALL(m, - SendRequest("http://foo.com",_,_)).Times(1).WillOnce(Return(true)); - GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar", - "1.0", - "AAA-BBB", - "", - "", - "test@test.com", - "none", - tempfn, - "http://foo.com", - "", - "", - &m); - ASSERT_TRUE(uploader->Upload()); -} - - -TEST_F(GoogleCrashdumpUploaderTest, InvalidPathname) { - MockLibcurlWrapper m; - EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true)); - EXPECT_CALL(m, SendRequest(_,_,_)).Times(0); - GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar", - "1.0", - "AAA-BBB", - "", - "", - "test@test.com", - "none", - "/tmp/foo.dmp", - "http://foo.com", - "", - "", - &m); - ASSERT_FALSE(uploader->Upload()); -} - -TEST_F(GoogleCrashdumpUploaderTest, TestRequiredParametersMustBePresent) { - // Test with empty product name. - GoogleCrashdumpUploader uploader("", - "1.0", - "AAA-BBB", - "", - "", - "test@test.com", - "none", - "/tmp/foo.dmp", - "http://foo.com", - "", - ""); - ASSERT_FALSE(uploader.Upload()); - - // Test with empty product version. - GoogleCrashdumpUploader uploader1("product", - "", - "AAA-BBB", - "", - "", - "", - "", - "/tmp/foo.dmp", - "", - "", - ""); - - ASSERT_FALSE(uploader1.Upload()); - - // Test with empty client GUID. - GoogleCrashdumpUploader uploader2("product", - "1.0", - "", - "", - "", - "", - "", - "/tmp/foo.dmp", - "", - "", - ""); - ASSERT_FALSE(uploader2.Upload()); -} -} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.cc deleted file mode 100644 index 5bea3afa262..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.cc +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright (c) 2009, 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 -#include -#include -#include - -#include - -#include "common/linux/libcurl_wrapper.h" -#include "third_party/linux/include/glog/logging.h" - -namespace google_breakpad { -LibcurlWrapper::LibcurlWrapper() - : init_ok_(false), - formpost_(NULL), - lastptr_(NULL), - headerlist_(NULL) { - curl_lib_ = dlopen("libcurl.so", RTLD_NOW); - if (!curl_lib_) { - curl_lib_ = dlopen("libcurl.so.4", RTLD_NOW); - } - if (!curl_lib_) { - curl_lib_ = dlopen("libcurl.so.3", RTLD_NOW); - } - if (!curl_lib_) { - LOG(WARNING) << "Could not find libcurl via dlopen"; - return; - } - LOG(INFO) << "LibcurlWrapper init succeeded"; - init_ok_ = true; - return; -} - -bool LibcurlWrapper::SetProxy(const std::string& proxy_host, - const std::string& proxy_userpwd) { - if (!init_ok_) { - return false; - } - // Set proxy information if necessary. - if (!proxy_host.empty()) { - (*easy_setopt_)(curl_, CURLOPT_PROXY, proxy_host.c_str()); - } else { - LOG(WARNING) << "SetProxy called with empty proxy host."; - return false; - } - if (!proxy_userpwd.empty()) { - (*easy_setopt_)(curl_, CURLOPT_PROXYUSERPWD, proxy_userpwd.c_str()); - } else { - LOG(WARNING) << "SetProxy called with empty proxy username/password."; - return false; - } - LOG(INFO) << "Set proxy host to " << proxy_host; - return true; -} - -bool LibcurlWrapper::AddFile(const std::string& upload_file_path, - const std::string& basename) { - if (!init_ok_) { - return false; - } - LOG(INFO) << "Adding " << upload_file_path << " to form upload."; - // Add form file. - (*formadd_)(&formpost_, &lastptr_, - CURLFORM_COPYNAME, basename.c_str(), - CURLFORM_FILE, upload_file_path.c_str(), - CURLFORM_END); - - return true; -} - -// Callback to get the response data from server. -static size_t WriteCallback(void *ptr, size_t size, - size_t nmemb, void *userp) { - if (!userp) - return 0; - - std::string *response = reinterpret_cast(userp); - size_t real_size = size * nmemb; - response->append(reinterpret_cast(ptr), real_size); - return real_size; -} - -bool LibcurlWrapper::SendRequest(const std::string& url, - const std::map& parameters, - std::string* server_response) { - (*easy_setopt_)(curl_, CURLOPT_URL, url.c_str()); - std::map::const_iterator iter = parameters.begin(); - for (; iter != parameters.end(); ++iter) - (*formadd_)(&formpost_, &lastptr_, - CURLFORM_COPYNAME, iter->first.c_str(), - CURLFORM_COPYCONTENTS, iter->second.c_str(), - CURLFORM_END); - - (*easy_setopt_)(curl_, CURLOPT_HTTPPOST, formpost_); - if (server_response != NULL) { - (*easy_setopt_)(curl_, CURLOPT_WRITEFUNCTION, WriteCallback); - (*easy_setopt_)(curl_, CURLOPT_WRITEDATA, - reinterpret_cast(server_response)); - } - - CURLcode err_code = CURLE_OK; - err_code = (*easy_perform_)(curl_); - *(void**) (&easy_strerror_) = dlsym(curl_lib_, "curl_easy_strerror"); -#ifndef NDEBUG - if (err_code != CURLE_OK) - fprintf(stderr, "Failed to send http request to %s, error: %s\n", - url.c_str(), - (*easy_strerror_)(err_code)); -#endif - if (headerlist_ != NULL) { - (*slist_free_all_)(headerlist_); - } - - (*easy_cleanup_)(curl_); - if (formpost_ != NULL) { - (*formfree_)(formpost_); - } - - return err_code == CURLE_OK; -} - -bool LibcurlWrapper::Init() { - if (!init_ok_) { - LOG(WARNING) << "Init_OK was not true in LibcurlWrapper::Init(), check earlier log messages"; - return false; - } - - if (!SetFunctionPointers()) { - LOG(WARNING) << "Could not find function pointers"; - init_ok_ = false; - return false; - } - - curl_ = (*easy_init_)(); - - last_curl_error_ = "No Error"; - - if (!curl_) { - dlclose(curl_lib_); - LOG(WARNING) << "Curl initialization failed"; - return false; - } - - // Disable 100-continue header. - char buf[] = "Expect:"; - - headerlist_ = (*slist_append_)(headerlist_, buf); - (*easy_setopt_)(curl_, CURLOPT_HTTPHEADER, headerlist_); - return true; -} - -#define SET_AND_CHECK_FUNCTION_POINTER(var, function_name) \ - *(void**) (&var) = dlsym(curl_lib_, function_name); \ - if (!var) { \ - LOG(WARNING) << "Could not find libcurl function " << function_name; \ - init_ok_ = false; \ - return false; \ - } - -bool LibcurlWrapper::SetFunctionPointers() { - - SET_AND_CHECK_FUNCTION_POINTER(easy_init_, - "curl_easy_init"); - SET_AND_CHECK_FUNCTION_POINTER(easy_setopt_, - "curl_easy_setopt"); - SET_AND_CHECK_FUNCTION_POINTER(formadd_, - "curl_formadd"); - SET_AND_CHECK_FUNCTION_POINTER(slist_append_, - "curl_slist_append"); - SET_AND_CHECK_FUNCTION_POINTER(easy_perform_, - "curl_easy_perform"); - SET_AND_CHECK_FUNCTION_POINTER(easy_cleanup_, - "curl_easy_cleanup"); - SET_AND_CHECK_FUNCTION_POINTER(slist_free_all_, - "curl_slist_free_all"); - SET_AND_CHECK_FUNCTION_POINTER(formfree_, - "curl_formfree"); - return true; -} - -} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.h b/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.h deleted file mode 100644 index 08aa958640c..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.h +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 2009, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// A wrapper for libcurl to do HTTP Uploads, to support easy mocking -// and unit testing of the HTTPUpload class. - -#include -#include -#include - -namespace google_breakpad { -class LibcurlWrapper { - public: - LibcurlWrapper(); - virtual bool Init(); - virtual bool SetProxy(const std::string& proxy_host, - const std::string& proxy_userpwd); - virtual bool AddFile(const std::string& upload_file_path, - const std::string& basename); - virtual bool SendRequest(const std::string& url, - const std::map& parameters, - std::string* server_response); - private: - // This function initializes class state corresponding to function - // pointers into the CURL library. - bool SetFunctionPointers(); - - bool init_ok_; // Whether init succeeded - void* curl_lib_; // Pointer to result of dlopen() on - // curl library - std::string last_curl_error_; // The text of the last error when - // dealing - // with CURL. - - CURL *curl_; // Pointer for handle for CURL calls. - - CURL* (*easy_init_)(void); - - // Stateful pointers for calling into curl_formadd() - struct curl_httppost *formpost_; - struct curl_httppost *lastptr_; - struct curl_slist *headerlist_; - - // Function pointers into CURL library - CURLcode (*easy_setopt_)(CURL *, CURLoption, ...); - CURLFORMcode (*formadd_)(struct curl_httppost **, - struct curl_httppost **, ...); - struct curl_slist* (*slist_append_)(struct curl_slist *, const char *); - void (*slist_free_all_)(struct curl_slist *); - CURLcode (*easy_perform_)(CURL *); - const char* (*easy_strerror_)(CURLcode); - void (*easy_cleanup_)(CURL *); - void (*formfree_)(struct curl_httppost *); - -}; -} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.h b/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.h deleted file mode 100644 index e08f27f7135..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.h +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) 2009, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// This header provides replacements for libc functions that we need. We if -// call the libc functions directly we risk crashing in the dynamic linker as -// it tries to resolve uncached PLT entries. - -#ifndef CLIENT_LINUX_LINUX_LIBC_SUPPORT_H_ -#define CLIENT_LINUX_LINUX_LIBC_SUPPORT_H_ - -#include -#include -#include - -extern "C" { - -static inline size_t -my_strlen(const char* s) { - size_t len = 0; - while (*s++) len++; - return len; -} - -static inline int -my_strcmp(const char* a, const char* b) { - for (;;) { - if (*a < *b) - return -1; - else if (*a > *b) - return 1; - else if (*a == 0) - return 0; - a++; - b++; - } -} - -static inline int -my_strncmp(const char* a, const char* b, size_t len) { - for (size_t i = 0; i < len; ++i) { - if (*a < *b) - return -1; - else if (*a > *b) - return 1; - else if (*a == 0) - return 0; - a++; - b++; - } - - return 0; -} - -// Parse a non-negative integer. -// result: (output) the resulting non-negative integer -// s: a NUL terminated string -// Return true iff successful. -static inline bool -my_strtoui(int* result, const char* s) { - if (*s == 0) - return false; - int r = 0; - for (;; s++) { - if (*s == 0) - break; - const int old_r = r; - r *= 10; - if (*s < '0' || *s > '9') - return false; - r += *s - '0'; - if (r < old_r) - return false; - } - - *result = r; - return true; -} - -// Return the length of the given, non-negative integer when expressed in base -// 10. -static inline unsigned -my_int_len(int i) { - if (!i) - return 1; - - int len = 0; - while (i) { - len++; - i /= 10; - } - - return len; -} - -// Convert a non-negative integer to a string -// output: (output) the resulting string is written here. This buffer must be -// large enough to hold the resulting string. Call |my_int_len| to get the -// required length. -// i: the non-negative integer to serialise. -// i_len: the length of the integer in base 10 (see |my_int_len|). -static inline void -my_itos(char* output, int i, unsigned i_len) { - for (unsigned index = i_len; index; --index, i /= 10) - output[index - 1] = '0' + (i % 10); -} - -static inline const char* -my_strchr(const char* haystack, char needle) { - while (*haystack && *haystack != needle) - haystack++; - if (*haystack == needle) - return haystack; - return (const char*) 0; -} - -// Read a hex value -// result: (output) the resulting value -// s: a string -// Returns a pointer to the first invalid charactor. -static inline const char* -my_read_hex_ptr(uintptr_t* result, const char* s) { - uintptr_t r = 0; - - for (;; ++s) { - if (*s >= '0' && *s <= '9') { - r <<= 4; - r += *s - '0'; - } else if (*s >= 'a' && *s <= 'f') { - r <<= 4; - r += (*s - 'a') + 10; - } else if (*s >= 'A' && *s <= 'F') { - r <<= 4; - r += (*s - 'A') + 10; - } else { - break; - } - } - - *result = r; - return s; -} - -static inline void -my_memset(void* ip, char c, size_t len) { - char* p = (char *) ip; - while (len--) - *p++ = c; -} - -} // extern "C" - -#endif // CLIENT_LINUX_LINUX_LIBC_SUPPORT_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support_unittest.cc deleted file mode 100644 index d3907e947a7..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support_unittest.cc +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright (c) 2009, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "common/linux/linux_libc_support.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { -typedef testing::Test LinuxLibcSupportTest; -} - -TEST(LinuxLibcSupportTest, strlen) { - static const char* test_data[] = { "", "a", "aa", "aaa", "aabc", NULL }; - for (unsigned i = 0; ; ++i) { - if (!test_data[i]) - break; - ASSERT_EQ(strlen(test_data[i]), my_strlen(test_data[i])); - } -} - -TEST(LinuxLibcSupportTest, strcmp) { - static const char* test_data[] = { - "", "", - "a", "", - "", "a", - "a", "b", - "a", "a", - "ab", "aa", - "abc", "ab", - "abc", "abc", - NULL, - }; - - for (unsigned i = 0; ; ++i) { - if (!test_data[i*2]) - break; - ASSERT_EQ(my_strcmp(test_data[i*2], test_data[i*2 + 1]), - strcmp(test_data[i*2], test_data[i*2 + 1])); - } -} - -TEST(LinuxLibcSupportTest, strtoui) { - int result; - - ASSERT_FALSE(my_strtoui(&result, "")); - ASSERT_FALSE(my_strtoui(&result, "-1")); - ASSERT_FALSE(my_strtoui(&result, "-")); - ASSERT_FALSE(my_strtoui(&result, "a")); - ASSERT_FALSE(my_strtoui(&result, "23472893472938472987987398472398")); - - ASSERT_TRUE(my_strtoui(&result, "0")); - ASSERT_EQ(result, 0); - ASSERT_TRUE(my_strtoui(&result, "1")); - ASSERT_EQ(result, 1); - ASSERT_TRUE(my_strtoui(&result, "12")); - ASSERT_EQ(result, 12); - ASSERT_TRUE(my_strtoui(&result, "123")); - ASSERT_EQ(result, 123); - ASSERT_TRUE(my_strtoui(&result, "0123")); - ASSERT_EQ(result, 123); -} - -TEST(LinuxLibcSupportTest, int_len) { - ASSERT_EQ(my_int_len(0), 1); - ASSERT_EQ(my_int_len(2), 1); - ASSERT_EQ(my_int_len(5), 1); - ASSERT_EQ(my_int_len(9), 1); - ASSERT_EQ(my_int_len(10), 2); - ASSERT_EQ(my_int_len(99), 2); - ASSERT_EQ(my_int_len(100), 3); - ASSERT_EQ(my_int_len(101), 3); - ASSERT_EQ(my_int_len(1000), 4); -} - -TEST(LinuxLibcSupportTest, itos) { - char buf[10]; - - my_itos(buf, 0, 1); - ASSERT_EQ(0, memcmp(buf, "0", 1)); - - my_itos(buf, 1, 1); - ASSERT_EQ(0, memcmp(buf, "1", 1)); - - my_itos(buf, 10, 2); - ASSERT_EQ(0, memcmp(buf, "10", 2)); - - my_itos(buf, 63, 2); - ASSERT_EQ(0, memcmp(buf, "63", 2)); - - my_itos(buf, 101, 3); - ASSERT_EQ(0, memcmp(buf, "101", 2)); -} - -TEST(LinuxLibcSupportTest, strchr) { - ASSERT_EQ(NULL, my_strchr("abc", 'd')); - ASSERT_EQ(NULL, my_strchr("", 'd')); - ASSERT_EQ(NULL, my_strchr("efghi", 'd')); - - ASSERT_TRUE(my_strchr("a", 'a')); - ASSERT_TRUE(my_strchr("abc", 'a')); - ASSERT_TRUE(my_strchr("bcda", 'a')); - ASSERT_TRUE(my_strchr("sdfasdf", 'a')); -} - -TEST(LinuxLibcSupportTest, read_hex_ptr) { - uintptr_t result; - const char* last; - - last = my_read_hex_ptr(&result, ""); - ASSERT_EQ(result, 0); - ASSERT_EQ(*last, 0); - - last = my_read_hex_ptr(&result, "0"); - ASSERT_EQ(result, 0); - ASSERT_EQ(*last, 0); - - last = my_read_hex_ptr(&result, "0123"); - ASSERT_EQ(result, 0x123); - ASSERT_EQ(*last, 0); - - last = my_read_hex_ptr(&result, "0123a"); - ASSERT_EQ(result, 0x123a); - ASSERT_EQ(*last, 0); - - last = my_read_hex_ptr(&result, "0123a-"); - ASSERT_EQ(result, 0x123a); - ASSERT_EQ(*last, '-'); -} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/linux_syscall_support.h b/toolkit/crashreporter/google-breakpad/src/common/linux/linux_syscall_support.h deleted file mode 100644 index f95400dc169..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/linux_syscall_support.h +++ /dev/null @@ -1,2800 +0,0 @@ -/* Copyright (c) 2005-2008, 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. - * - * --- - * Author: Markus Gutschke - */ - -/* This file includes Linux-specific support functions common to the - * coredumper and the thread lister; primarily, this is a collection - * of direct system calls, and a couple of symbols missing from - * standard header files. - * There are a few options that the including file can set to control - * the behavior of this file: - * - * SYS_CPLUSPLUS: - * The entire header file will normally be wrapped in 'extern "C" { }", - * making it suitable for compilation as both C and C++ source. If you - * do not want to do this, you can set the SYS_CPLUSPLUS macro to inhibit - * the wrapping. N.B. doing so will suppress inclusion of all prerequisite - * system header files, too. It is the caller's responsibility to provide - * the necessary definitions. - * - * SYS_ERRNO: - * All system calls will update "errno" unless overriden by setting the - * SYS_ERRNO macro prior to including this file. SYS_ERRNO should be - * an l-value. - * - * SYS_INLINE: - * New symbols will be defined "static inline", unless overridden by - * the SYS_INLINE macro. - * - * SYS_LINUX_SYSCALL_SUPPORT_H - * This macro is used to avoid multiple inclusions of this header file. - * If you need to include this file more than once, make sure to - * unset SYS_LINUX_SYSCALL_SUPPORT_H before each inclusion. - * - * SYS_PREFIX: - * New system calls will have a prefix of "sys_" unless overridden by - * the SYS_PREFIX macro. Valid values for this macro are [0..9] which - * results in prefixes "sys[0..9]_". It is also possible to set this - * macro to -1, which avoids all prefixes. - * - * This file defines a few internal symbols that all start with "LSS_". - * Do not access these symbols from outside this file. They are not part - * of the supported API. - */ -#ifndef SYS_LINUX_SYSCALL_SUPPORT_H -#define SYS_LINUX_SYSCALL_SUPPORT_H - -/* We currently only support x86-32, x86-64, ARM, MIPS, and PPC on Linux. - * Porting to other related platforms should not be difficult. - */ -#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || \ - defined(__mips__) || defined(__PPC__)) && defined(__linux) - -#ifndef SYS_CPLUSPLUS -#ifdef __cplusplus -/* Some system header files in older versions of gcc neglect to properly - * handle being included from C++. As it appears to be harmless to have - * multiple nested 'extern "C"' blocks, just add another one here. - */ -extern "C" { -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __mips__ -/* Include definitions of the ABI currently in use. */ -#include -#endif - -#endif - -/* As glibc often provides subtly incompatible data structures (and implicit - * wrapper functions that convert them), we provide our own kernel data - * structures for use by the system calls. - * These structures have been developed by using Linux 2.6.23 headers for - * reference. Note though, we do not care about exact API compatibility - * with the kernel, and in fact the kernel often does not have a single - * API that works across architectures. Instead, we try to mimic the glibc - * API where reasonable, and only guarantee ABI compatibility with the - * kernel headers. - * Most notably, here are a few changes that were made to the structures - * defined by kernel headers: - * - * - we only define structures, but not symbolic names for kernel data - * types. For the latter, we directly use the native C datatype - * (i.e. "unsigned" instead of "mode_t"). - * - in a few cases, it is possible to define identical structures for - * both 32bit (e.g. i386) and 64bit (e.g. x86-64) platforms by - * standardizing on the 64bit version of the data types. In particular, - * this means that we use "unsigned" where the 32bit headers say - * "unsigned long". - * - overall, we try to minimize the number of cases where we need to - * conditionally define different structures. - * - the "struct kernel_sigaction" class of structures have been - * modified to more closely mimic glibc's API by introducing an - * anonymous union for the function pointer. - * - a small number of field names had to have an underscore appended to - * them, because glibc defines a global macro by the same name. - */ - -/* include/linux/dirent.h */ -struct kernel_dirent64 { - unsigned long long d_ino; - long long d_off; - unsigned short d_reclen; - unsigned char d_type; - char d_name[256]; -}; - -/* include/linux/dirent.h */ -struct kernel_dirent { - long d_ino; - long d_off; - unsigned short d_reclen; - char d_name[256]; -}; - -/* include/linux/uio.h */ -struct kernel_iovec { - void *iov_base; - unsigned long iov_len; -}; - -/* include/linux/socket.h */ -struct kernel_msghdr { - void *msg_name; - int msg_namelen; - struct kernel_iovec*msg_iov; - unsigned long msg_iovlen; - void *msg_control; - unsigned long msg_controllen; - unsigned msg_flags; -}; - -/* include/asm-generic/poll.h */ -struct kernel_pollfd { - int fd; - short events; - short revents; -}; - -/* include/linux/resource.h */ -struct kernel_rlimit { - unsigned long rlim_cur; - unsigned long rlim_max; -}; - -/* include/linux/time.h */ -struct kernel_timespec { - long tv_sec; - long tv_nsec; -}; - -/* include/linux/time.h */ -struct kernel_timeval { - long tv_sec; - long tv_usec; -}; - -/* include/linux/resource.h */ -struct kernel_rusage { - struct kernel_timeval ru_utime; - struct kernel_timeval ru_stime; - long ru_maxrss; - long ru_ixrss; - long ru_idrss; - long ru_isrss; - long ru_minflt; - long ru_majflt; - long ru_nswap; - long ru_inblock; - long ru_oublock; - long ru_msgsnd; - long ru_msgrcv; - long ru_nsignals; - long ru_nvcsw; - long ru_nivcsw; -}; - -struct siginfo; -#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__PPC__) - -/* include/asm-{arm,i386,mips,ppc}/signal.h */ -struct kernel_old_sigaction { - union { - void (*sa_handler_)(int); - void (*sa_sigaction_)(int, struct siginfo *, void *); - }; - unsigned long sa_mask; - unsigned long sa_flags; - void (*sa_restorer)(void); -} __attribute__((packed,aligned(4))); -#elif (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) - #define kernel_old_sigaction kernel_sigaction -#endif - -/* Some kernel functions (e.g. sigaction() in 2.6.23) require that the - * exactly match the size of the signal set, even though the API was - * intended to be extensible. We define our own KERNEL_NSIG to deal with - * this. - * Please note that glibc provides signals [1.._NSIG-1], whereas the - * kernel (and this header) provides the range [1..KERNEL_NSIG]. The - * actual number of signals is obviously the same, but the constants - * differ by one. - */ -#ifdef __mips__ -#define KERNEL_NSIG 128 -#else -#define KERNEL_NSIG 64 -#endif - -/* include/asm-{arm,i386,mips,x86_64}/signal.h */ -struct kernel_sigset_t { - unsigned long sig[(KERNEL_NSIG + 8*sizeof(unsigned long) - 1)/ - (8*sizeof(unsigned long))]; -}; - -/* include/asm-{arm,i386,mips,x86_64,ppc}/signal.h */ -struct kernel_sigaction { -#ifdef __mips__ - unsigned long sa_flags; - union { - void (*sa_handler_)(int); - void (*sa_sigaction_)(int, struct siginfo *, void *); - }; - struct kernel_sigset_t sa_mask; -#else - union { - void (*sa_handler_)(int); - void (*sa_sigaction_)(int, struct siginfo *, void *); - }; - unsigned long sa_flags; - void (*sa_restorer)(void); - struct kernel_sigset_t sa_mask; -#endif -}; - -/* include/linux/socket.h */ -struct kernel_sockaddr { - unsigned short sa_family; - char sa_data[14]; -}; - -/* include/asm-{arm,i386,mips,ppc}/stat.h */ -#ifdef __mips__ -#if _MIPS_SIM == _MIPS_SIM_ABI64 -struct kernel_stat { -#else -struct kernel_stat64 { -#endif - unsigned st_dev; - unsigned __pad0[3]; - unsigned long long st_ino; - unsigned st_mode; - unsigned st_nlink; - unsigned st_uid; - unsigned st_gid; - unsigned st_rdev; - unsigned __pad1[3]; - long long st_size; - unsigned st_atime_; - unsigned st_atime_nsec_; - unsigned st_mtime_; - unsigned st_mtime_nsec_; - unsigned st_ctime_; - unsigned st_ctime_nsec_; - unsigned st_blksize; - unsigned __pad2; - unsigned long long st_blocks; -}; -#elif defined __PPC__ -struct kernel_stat64 { - unsigned long long st_dev; - unsigned long long st_ino; - unsigned st_mode; - unsigned st_nlink; - unsigned st_uid; - unsigned st_gid; - unsigned long long st_rdev; - unsigned short int __pad2; - long long st_size; - long st_blksize; - long long st_blocks; - long st_atime_; - unsigned long st_atime_nsec_; - long st_mtime_; - unsigned long st_mtime_nsec_; - long st_ctime_; - unsigned long st_ctime_nsec_; - unsigned long __unused4; - unsigned long __unused5; -}; -#else -struct kernel_stat64 { - unsigned long long st_dev; - unsigned char __pad0[4]; - unsigned __st_ino; - unsigned st_mode; - unsigned st_nlink; - unsigned st_uid; - unsigned st_gid; - unsigned long long st_rdev; - unsigned char __pad3[4]; - long long st_size; - unsigned st_blksize; - unsigned long long st_blocks; - unsigned st_atime_; - unsigned st_atime_nsec_; - unsigned st_mtime_; - unsigned st_mtime_nsec_; - unsigned st_ctime_; - unsigned st_ctime_nsec_; - unsigned long long st_ino; -}; -#endif - -/* include/asm-{arm,i386,mips,x86_64,ppc}/stat.h */ -#if defined(__i386__) || defined(__ARM_ARCH_3__) -struct kernel_stat { - /* The kernel headers suggest that st_dev and st_rdev should be 32bit - * quantities encoding 12bit major and 20bit minor numbers in an interleaved - * format. In reality, we do not see useful data in the top bits. So, - * we'll leave the padding in here, until we find a better solution. - */ - unsigned short st_dev; - short pad1; - unsigned st_ino; - unsigned short st_mode; - unsigned short st_nlink; - unsigned short st_uid; - unsigned short st_gid; - unsigned short st_rdev; - short pad2; - unsigned st_size; - unsigned st_blksize; - unsigned st_blocks; - unsigned st_atime_; - unsigned st_atime_nsec_; - unsigned st_mtime_; - unsigned st_mtime_nsec_; - unsigned st_ctime_; - unsigned st_ctime_nsec_; - unsigned __unused4; - unsigned __unused5; -}; -#elif defined(__x86_64__) -struct kernel_stat { - unsigned long st_dev; - unsigned long st_ino; - unsigned long st_nlink; - unsigned st_mode; - unsigned st_uid; - unsigned st_gid; - unsigned __pad0; - unsigned long st_rdev; - long st_size; - long st_blksize; - long st_blocks; - unsigned long st_atime_; - unsigned long st_atime_nsec_; - unsigned long st_mtime_; - unsigned long st_mtime_nsec_; - unsigned long st_ctime_; - unsigned long st_ctime_nsec_; - long __unused[3]; -}; -#elif defined(__PPC__) -struct kernel_stat { - unsigned st_dev; - unsigned long st_ino; // ino_t - unsigned long st_mode; // mode_t - unsigned short st_nlink; // nlink_t - unsigned st_uid; // uid_t - unsigned st_gid; // gid_t - unsigned st_rdev; - long st_size; // off_t - unsigned long st_blksize; - unsigned long st_blocks; - unsigned long st_atime_; - unsigned long st_atime_nsec_; - unsigned long st_mtime_; - unsigned long st_mtime_nsec_; - unsigned long st_ctime_; - unsigned long st_ctime_nsec_; - unsigned long __unused4; - unsigned long __unused5; -}; -#elif (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64) -struct kernel_stat { - unsigned st_dev; - int st_pad1[3]; - unsigned st_ino; - unsigned st_mode; - unsigned st_nlink; - unsigned st_uid; - unsigned st_gid; - unsigned st_rdev; - int st_pad2[2]; - long st_size; - int st_pad3; - long st_atime_; - long st_atime_nsec_; - long st_mtime_; - long st_mtime_nsec_; - long st_ctime_; - long st_ctime_nsec_; - int st_blksize; - int st_blocks; - int st_pad4[14]; -}; -#endif - -/* include/asm-{arm,i386,mips,x86_64,ppc}/statfs.h */ -#ifdef __mips__ -#if _MIPS_SIM != _MIPS_SIM_ABI64 -struct kernel_statfs64 { - unsigned long f_type; - unsigned long f_bsize; - unsigned long f_frsize; - unsigned long __pad; - unsigned long long f_blocks; - unsigned long long f_bfree; - unsigned long long f_files; - unsigned long long f_ffree; - unsigned long long f_bavail; - struct { int val[2]; } f_fsid; - unsigned long f_namelen; - unsigned long f_spare[6]; -}; -#endif -#elif !defined(__x86_64__) -struct kernel_statfs64 { - unsigned long f_type; - unsigned long f_bsize; - unsigned long long f_blocks; - unsigned long long f_bfree; - unsigned long long f_bavail; - unsigned long long f_files; - unsigned long long f_ffree; - struct { int val[2]; } f_fsid; - unsigned long f_namelen; - unsigned long f_frsize; - unsigned long f_spare[5]; -}; -#endif - -/* include/asm-{arm,i386,mips,x86_64,ppc,generic}/statfs.h */ -#ifdef __mips__ -struct kernel_statfs { - long f_type; - long f_bsize; - long f_frsize; - long f_blocks; - long f_bfree; - long f_files; - long f_ffree; - long f_bavail; - struct { int val[2]; } f_fsid; - long f_namelen; - long f_spare[6]; -}; -#else -struct kernel_statfs { - /* x86_64 actually defines all these fields as signed, whereas all other */ - /* platforms define them as unsigned. Leaving them at unsigned should not */ - /* cause any problems. */ - unsigned long f_type; - unsigned long f_bsize; - unsigned long f_blocks; - unsigned long f_bfree; - unsigned long f_bavail; - unsigned long f_files; - unsigned long f_ffree; - struct { int val[2]; } f_fsid; - unsigned long f_namelen; - unsigned long f_frsize; - unsigned long f_spare[5]; -}; -#endif - - -/* Definitions missing from the standard header files */ -#ifndef O_DIRECTORY -#if defined(__ARM_ARCH_3__) -#define O_DIRECTORY 0040000 -#else -#define O_DIRECTORY 0200000 -#endif -#endif -#ifndef NT_PRXFPREG -#define NT_PRXFPREG 0x46e62b7f -#endif -#ifndef PTRACE_GETFPXREGS -#define PTRACE_GETFPXREGS ((enum __ptrace_request)18) -#endif -#ifndef PR_GET_DUMPABLE -#define PR_GET_DUMPABLE 3 -#endif -#ifndef PR_SET_DUMPABLE -#define PR_SET_DUMPABLE 4 -#endif -#ifndef AT_FDCWD -#define AT_FDCWD (-100) -#endif -#ifndef AT_SYMLINK_NOFOLLOW -#define AT_SYMLINK_NOFOLLOW 0x100 -#endif -#ifndef AT_REMOVEDIR -#define AT_REMOVEDIR 0x200 -#endif -#ifndef MREMAP_FIXED -#define MREMAP_FIXED 2 -#endif -#ifndef SA_RESTORER -#define SA_RESTORER 0x04000000 -#endif - -#if defined(__i386__) -#ifndef __NR_setresuid -#define __NR_setresuid 164 -#define __NR_setresgid 170 -#endif -#ifndef __NR_rt_sigaction -#define __NR_rt_sigaction 174 -#define __NR_rt_sigprocmask 175 -#define __NR_rt_sigpending 176 -#define __NR_rt_sigsuspend 179 -#endif -#ifndef __NR_pread64 -#define __NR_pread64 180 -#endif -#ifndef __NR_pwrite64 -#define __NR_pwrite64 181 -#endif -#ifndef __NR_ugetrlimit -#define __NR_ugetrlimit 191 -#endif -#ifndef __NR_stat64 -#define __NR_stat64 195 -#endif -#ifndef __NR_fstat64 -#define __NR_fstat64 197 -#endif -#ifndef __NR_setresuid32 -#define __NR_setresuid32 208 -#define __NR_setresgid32 210 -#endif -#ifndef __NR_setfsuid32 -#define __NR_setfsuid32 215 -#define __NR_setfsgid32 216 -#endif -#ifndef __NR_getdents64 -#define __NR_getdents64 220 -#endif -#ifndef __NR_gettid -#define __NR_gettid 224 -#endif -#ifndef __NR_readahead -#define __NR_readahead 225 -#endif -#ifndef __NR_setxattr -#define __NR_setxattr 226 -#endif -#ifndef __NR_lsetxattr -#define __NR_lsetxattr 227 -#endif -#ifndef __NR_getxattr -#define __NR_getxattr 229 -#endif -#ifndef __NR_lgetxattr -#define __NR_lgetxattr 230 -#endif -#ifndef __NR_futex -#define __NR_futex 240 -#endif -#ifndef __NR_sched_setaffinity -#define __NR_sched_setaffinity 241 -#define __NR_sched_getaffinity 242 -#endif -#ifndef __NR_set_tid_address -#define __NR_set_tid_address 258 -#endif -#ifndef __NR_statfs64 -#define __NR_statfs64 268 -#endif -#ifndef __NR_fstatfs64 -#define __NR_fstatfs64 269 -#endif -#ifndef __NR_fadvise64_64 -#define __NR_fadvise64_64 272 -#endif -#ifndef __NR_openat -#define __NR_openat 295 -#endif -#ifndef __NR_fstatat64 -#define __NR_fstatat64 300 -#endif -#ifndef __NR_unlinkat -#define __NR_unlinkat 301 -#endif -#ifndef __NR_move_pages -#define __NR_move_pages 317 -#endif -/* End of i386 definitions */ -#elif defined(__ARM_ARCH_3__) -#ifndef __NR_setresuid -#define __NR_setresuid (__NR_SYSCALL_BASE + 164) -#define __NR_setresgid (__NR_SYSCALL_BASE + 170) -#endif -#ifndef __NR_rt_sigaction -#define __NR_rt_sigaction (__NR_SYSCALL_BASE + 174) -#define __NR_rt_sigprocmask (__NR_SYSCALL_BASE + 175) -#define __NR_rt_sigpending (__NR_SYSCALL_BASE + 176) -#define __NR_rt_sigsuspend (__NR_SYSCALL_BASE + 179) -#endif -#ifndef __NR_pread64 -#define __NR_pread64 (__NR_SYSCALL_BASE + 180) -#endif -#ifndef __NR_pwrite64 -#define __NR_pwrite64 (__NR_SYSCALL_BASE + 181) -#endif -#ifndef __NR_ugetrlimit -#define __NR_ugetrlimit (__NR_SYSCALL_BASE + 191) -#endif -#ifndef __NR_stat64 -#define __NR_stat64 (__NR_SYSCALL_BASE + 195) -#endif -#ifndef __NR_fstat64 -#define __NR_fstat64 (__NR_SYSCALL_BASE + 197) -#endif -#ifndef __NR_setresuid32 -#define __NR_setresuid32 (__NR_SYSCALL_BASE + 208) -#define __NR_setresgid32 (__NR_SYSCALL_BASE + 210) -#endif -#ifndef __NR_setfsuid32 -#define __NR_setfsuid32 (__NR_SYSCALL_BASE + 215) -#define __NR_setfsgid32 (__NR_SYSCALL_BASE + 216) -#endif -#ifndef __NR_getdents64 -#define __NR_getdents64 (__NR_SYSCALL_BASE + 217) -#endif -#ifndef __NR_gettid -#define __NR_gettid (__NR_SYSCALL_BASE + 224) -#endif -#ifndef __NR_readahead -#define __NR_readahead (__NR_SYSCALL_BASE + 225) -#endif -#ifndef __NR_setxattr -#define __NR_setxattr (__NR_SYSCALL_BASE + 226) -#endif -#ifndef __NR_lsetxattr -#define __NR_lsetxattr (__NR_SYSCALL_BASE + 227) -#endif -#ifndef __NR_getxattr -#define __NR_getxattr (__NR_SYSCALL_BASE + 229) -#endif -#ifndef __NR_lgetxattr -#define __NR_lgetxattr (__NR_SYSCALL_BASE + 230) -#endif -#ifndef __NR_futex -#define __NR_futex (__NR_SYSCALL_BASE + 240) -#endif -#ifndef __NR_sched_setaffinity -#define __NR_sched_setaffinity (__NR_SYSCALL_BASE + 241) -#define __NR_sched_getaffinity (__NR_SYSCALL_BASE + 242) -#endif -#ifndef __NR_set_tid_address -#define __NR_set_tid_address (__NR_SYSCALL_BASE + 256) -#endif -#ifndef __NR_statfs64 -#define __NR_statfs64 (__NR_SYSCALL_BASE + 266) -#endif -#ifndef __NR_fstatfs64 -#define __NR_fstatfs64 (__NR_SYSCALL_BASE + 267) -#endif -#ifndef __NR_move_pages -#define __NR_move_pages (__NR_SYSCALL_BASE + 344) -#endif -/* End of ARM 3 definitions */ -#elif defined(__x86_64__) -#ifndef __NR_setresuid -#define __NR_setresuid 117 -#define __NR_setresgid 119 -#endif -#ifndef __NR_gettid -#define __NR_gettid 186 -#endif -#ifndef __NR_readahead -#define __NR_readahead 187 -#endif -#ifndef __NR_setxattr -#define __NR_setxattr 188 -#endif -#ifndef __NR_lsetxattr -#define __NR_lsetxattr 189 -#endif -#ifndef __NR_getxattr -#define __NR_getxattr 191 -#endif -#ifndef __NR_lgetxattr -#define __NR_lgetxattr 192 -#endif -#ifndef __NR_futex -#define __NR_futex 202 -#endif -#ifndef __NR_sched_setaffinity -#define __NR_sched_setaffinity 203 -#define __NR_sched_getaffinity 204 -#endif -#ifndef __NR_getdents64 -#define __NR_getdents64 217 -#endif -#ifndef __NR_set_tid_address -#define __NR_set_tid_address 218 -#endif -#ifndef __NR_fadvise64 -#define __NR_fadvise64 221 -#endif -#ifndef __NR_openat -#define __NR_openat 257 -#endif -#ifndef __NR_newfstatat -#define __NR_newfstatat 262 -#endif -#ifndef __NR_unlinkat -#define __NR_unlinkat 263 -#endif -#ifndef __NR_move_pages -#define __NR_move_pages 279 -#endif -/* End of x86-64 definitions */ -#elif defined(__mips__) -#if _MIPS_SIM == _MIPS_SIM_ABI32 -#ifndef __NR_setresuid -#define __NR_setresuid (__NR_Linux + 185) -#define __NR_setresgid (__NR_Linux + 190) -#endif -#ifndef __NR_rt_sigaction -#define __NR_rt_sigaction (__NR_Linux + 194) -#define __NR_rt_sigprocmask (__NR_Linux + 195) -#define __NR_rt_sigpending (__NR_Linux + 196) -#define __NR_rt_sigsuspend (__NR_Linux + 199) -#endif -#ifndef __NR_pread64 -#define __NR_pread64 (__NR_Linux + 200) -#endif -#ifndef __NR_pwrite64 -#define __NR_pwrite64 (__NR_Linux + 201) -#endif -#ifndef __NR_stat64 -#define __NR_stat64 (__NR_Linux + 213) -#endif -#ifndef __NR_fstat64 -#define __NR_fstat64 (__NR_Linux + 215) -#endif -#ifndef __NR_getdents64 -#define __NR_getdents64 (__NR_Linux + 219) -#endif -#ifndef __NR_gettid -#define __NR_gettid (__NR_Linux + 222) -#endif -#ifndef __NR_readahead -#define __NR_readahead (__NR_Linux + 223) -#endif -#ifndef __NR_setxattr -#define __NR_setxattr (__NR_Linux + 224) -#endif -#ifndef __NR_lsetxattr -#define __NR_lsetxattr (__NR_Linux + 225) -#endif -#ifndef __NR_getxattr -#define __NR_getxattr (__NR_Linux + 227) -#endif -#ifndef __NR_lgetxattr -#define __NR_lgetxattr (__NR_Linux + 228) -#endif -#ifndef __NR_futex -#define __NR_futex (__NR_Linux + 238) -#endif -#ifndef __NR_sched_setaffinity -#define __NR_sched_setaffinity (__NR_Linux + 239) -#define __NR_sched_getaffinity (__NR_Linux + 240) -#endif -#ifndef __NR_set_tid_address -#define __NR_set_tid_address (__NR_Linux + 252) -#endif -#ifndef __NR_statfs64 -#define __NR_statfs64 (__NR_Linux + 255) -#endif -#ifndef __NR_fstatfs64 -#define __NR_fstatfs64 (__NR_Linux + 256) -#endif -#ifndef __NR_openat -#define __NR_openat (__NR_Linux + 288) -#endif -#ifndef __NR_fstatat -#define __NR_fstatat (__NR_Linux + 293) -#endif -#ifndef __NR_unlinkat -#define __NR_unlinkat (__NR_Linux + 294) -#endif -#ifndef __NR_move_pages -#define __NR_move_pages (__NR_Linux + 308) -#endif -/* End of MIPS (old 32bit API) definitions */ -#elif _MIPS_SIM == _MIPS_SIM_ABI64 -#ifndef __NR_setresuid -#define __NR_setresuid (__NR_Linux + 115) -#define __NR_setresgid (__NR_Linux + 117) -#endif -#ifndef __NR_gettid -#define __NR_gettid (__NR_Linux + 178) -#endif -#ifndef __NR_readahead -#define __NR_readahead (__NR_Linux + 179) -#endif -#ifndef __NR_setxattr -#define __NR_setxattr (__NR_Linux + 180) -#endif -#ifndef __NR_lsetxattr -#define __NR_lsetxattr (__NR_Linux + 181) -#endif -#ifndef __NR_getxattr -#define __NR_getxattr (__NR_Linux + 183) -#endif -#ifndef __NR_lgetxattr -#define __NR_lgetxattr (__NR_Linux + 184) -#endif -#ifndef __NR_futex -#define __NR_futex (__NR_Linux + 194) -#endif -#ifndef __NR_sched_setaffinity -#define __NR_sched_setaffinity (__NR_Linux + 195) -#define __NR_sched_getaffinity (__NR_Linux + 196) -#endif -#ifndef __NR_set_tid_address -#define __NR_set_tid_address (__NR_Linux + 212) -#endif -#ifndef __NR_openat -#define __NR_openat (__NR_Linux + 247) -#endif -#ifndef __NR_fstatat -#define __NR_fstatat (__NR_Linux + 252) -#endif -#ifndef __NR_unlinkat -#define __NR_unlinkat (__NR_Linux + 253) -#endif -#ifndef __NR_move_pages -#define __NR_move_pages (__NR_Linux + 267) -#endif -/* End of MIPS (64bit API) definitions */ -#else -#ifndef __NR_setresuid -#define __NR_setresuid (__NR_Linux + 115) -#define __NR_setresgid (__NR_Linux + 117) -#endif -#ifndef __NR_gettid -#define __NR_gettid (__NR_Linux + 178) -#endif -#ifndef __NR_readahead -#define __NR_readahead (__NR_Linux + 179) -#endif -#ifndef __NR_setxattr -#define __NR_setxattr (__NR_Linux + 180) -#endif -#ifndef __NR_lsetxattr -#define __NR_lsetxattr (__NR_Linux + 181) -#endif -#ifndef __NR_getxattr -#define __NR_getxattr (__NR_Linux + 183) -#endif -#ifndef __NR_lgetxattr -#define __NR_lgetxattr (__NR_Linux + 184) -#endif -#ifndef __NR_futex -#define __NR_futex (__NR_Linux + 194) -#endif -#ifndef __NR_sched_setaffinity -#define __NR_sched_setaffinity (__NR_Linux + 195) -#define __NR_sched_getaffinity (__NR_Linux + 196) -#endif -#ifndef __NR_set_tid_address -#define __NR_set_tid_address (__NR_Linux + 213) -#endif -#ifndef __NR_statfs64 -#define __NR_statfs64 (__NR_Linux + 217) -#endif -#ifndef __NR_fstatfs64 -#define __NR_fstatfs64 (__NR_Linux + 218) -#endif -#ifndef __NR_openat -#define __NR_openat (__NR_Linux + 251) -#endif -#ifndef __NR_fstatat -#define __NR_fstatat (__NR_Linux + 256) -#endif -#ifndef __NR_unlinkat -#define __NR_unlinkat (__NR_Linux + 257) -#endif -#ifndef __NR_move_pages -#define __NR_move_pages (__NR_Linux + 271) -#endif -/* End of MIPS (new 32bit API) definitions */ -#endif -/* End of MIPS definitions */ -#elif defined(__PPC__) -#ifndef __NR_setfsuid -#define __NR_setfsuid 138 -#define __NR_setfsgid 139 -#endif -#ifndef __NR_setresuid -#define __NR_setresuid 164 -#define __NR_setresgid 169 -#endif -#ifndef __NR_rt_sigaction -#define __NR_rt_sigaction 173 -#define __NR_rt_sigprocmask 174 -#define __NR_rt_sigpending 175 -#define __NR_rt_sigsuspend 178 -#endif -#ifndef __NR_pread64 -#define __NR_pread64 179 -#endif -#ifndef __NR_pwrite64 -#define __NR_pwrite64 180 -#endif -#ifndef __NR_ugetrlimit -#define __NR_ugetrlimit 190 -#endif -#ifndef __NR_readahead -#define __NR_readahead 191 -#endif -#ifndef __NR_stat64 -#define __NR_stat64 195 -#endif -#ifndef __NR_fstat64 -#define __NR_fstat64 197 -#endif -#ifndef __NR_getdents64 -#define __NR_getdents64 202 -#endif -#ifndef __NR_gettid -#define __NR_gettid 207 -#endif -#ifndef __NR_setxattr -#define __NR_setxattr 209 -#endif -#ifndef __NR_lsetxattr -#define __NR_lsetxattr 210 -#endif -#ifndef __NR_getxattr -#define __NR_getxattr 212 -#endif -#ifndef __NR_lgetxattr -#define __NR_lgetxattr 213 -#endif -#ifndef __NR_futex -#define __NR_futex 221 -#endif -#ifndef __NR_sched_setaffinity -#define __NR_sched_setaffinity 222 -#define __NR_sched_getaffinity 223 -#endif -#ifndef __NR_set_tid_address -#define __NR_set_tid_address 232 -#endif -#ifndef __NR_statfs64 -#define __NR_statfs64 252 -#endif -#ifndef __NR_fstatfs64 -#define __NR_fstatfs64 253 -#endif -#ifndef __NR_fadvise64_64 -#define __NR_fadvise64_64 254 -#endif -#ifndef __NR_openat -#define __NR_openat 286 -#endif -#ifndef __NR_fstatat64 -#define __NR_fstatat64 291 -#endif -#ifndef __NR_unlinkat -#define __NR_unlinkat 292 -#endif -#ifndef __NR_move_pages -#define __NR_move_pages 301 -#endif -/* End of powerpc defininitions */ -#endif - - -/* After forking, we must make sure to only call system calls. */ -#if __BOUNDED_POINTERS__ - #error "Need to port invocations of syscalls for bounded ptrs" -#else - /* The core dumper and the thread lister get executed after threads - * have been suspended. As a consequence, we cannot call any functions - * that acquire locks. Unfortunately, libc wraps most system calls - * (e.g. in order to implement pthread_atfork, and to make calls - * cancellable), which means we cannot call these functions. Instead, - * we have to call syscall() directly. - */ - #undef LSS_ERRNO - #ifdef SYS_ERRNO - /* Allow the including file to override the location of errno. This can - * be useful when using clone() with the CLONE_VM option. - */ - #define LSS_ERRNO SYS_ERRNO - #else - #define LSS_ERRNO errno - #endif - - #undef LSS_INLINE - #ifdef SYS_INLINE - #define LSS_INLINE SYS_INLINE - #else - #define LSS_INLINE static inline - #endif - - /* Allow the including file to override the prefix used for all new - * system calls. By default, it will be set to "sys_". - */ - #undef LSS_NAME - #ifndef SYS_PREFIX - #define LSS_NAME(name) sys_##name - #elif SYS_PREFIX < 0 - #define LSS_NAME(name) name - #elif SYS_PREFIX == 0 - #define LSS_NAME(name) sys0_##name - #elif SYS_PREFIX == 1 - #define LSS_NAME(name) sys1_##name - #elif SYS_PREFIX == 2 - #define LSS_NAME(name) sys2_##name - #elif SYS_PREFIX == 3 - #define LSS_NAME(name) sys3_##name - #elif SYS_PREFIX == 4 - #define LSS_NAME(name) sys4_##name - #elif SYS_PREFIX == 5 - #define LSS_NAME(name) sys5_##name - #elif SYS_PREFIX == 6 - #define LSS_NAME(name) sys6_##name - #elif SYS_PREFIX == 7 - #define LSS_NAME(name) sys7_##name - #elif SYS_PREFIX == 8 - #define LSS_NAME(name) sys8_##name - #elif SYS_PREFIX == 9 - #define LSS_NAME(name) sys9_##name - #endif - - #undef LSS_RETURN - #if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__)) - /* Failing system calls return a negative result in the range of - * -1..-4095. These are "errno" values with the sign inverted. - */ - #define LSS_RETURN(type, res) \ - do { \ - if ((unsigned long)(res) >= (unsigned long)(-4095)) { \ - LSS_ERRNO = -(res); \ - res = -1; \ - } \ - return (type) (res); \ - } while (0) - #elif defined(__mips__) - /* On MIPS, failing system calls return -1, and set errno in a - * separate CPU register. - */ - #define LSS_RETURN(type, res, err) \ - do { \ - if (err) { \ - LSS_ERRNO = (res); \ - res = -1; \ - } \ - return (type) (res); \ - } while (0) - #elif defined(__PPC__) - /* On PPC, failing system calls return -1, and set errno in a - * separate CPU register. See linux/unistd.h. - */ - #define LSS_RETURN(type, res, err) \ - do { \ - if (err & 0x10000000 ) { \ - LSS_ERRNO = (res); \ - res = -1; \ - } \ - return (type) (res); \ - } while (0) - #endif - #if defined(__i386__) - /* In PIC mode (e.g. when building shared libraries), gcc for i386 - * reserves ebx. Unfortunately, most distribution ship with implementations - * of _syscallX() which clobber ebx. - * Also, most definitions of _syscallX() neglect to mark "memory" as being - * clobbered. This causes problems with compilers, that do a better job - * at optimizing across __asm__ calls. - * So, we just have to redefine all of the _syscallX() macros. - */ - #undef LSS_BODY - #define LSS_BODY(type,args...) \ - long __res; \ - __asm__ __volatile__("push %%ebx\n" \ - "movl %2,%%ebx\n" \ - "int $0x80\n" \ - "pop %%ebx" \ - args \ - : "memory"); \ - LSS_RETURN(type,__res) - #undef _syscall0 - #define _syscall0(type,name) \ - type LSS_NAME(name)(void) { \ - long __res; \ - __asm__ volatile("int $0x80" \ - : "=a" (__res) \ - : "0" (__NR_##name) \ - : "memory"); \ - LSS_RETURN(type,__res); \ - } - #undef _syscall1 - #define _syscall1(type,name,type1,arg1) \ - type LSS_NAME(name)(type1 arg1) { \ - LSS_BODY(type, \ - : "=a" (__res) \ - : "0" (__NR_##name), "ri" ((long)(arg1))); \ - } - #undef _syscall2 - #define _syscall2(type,name,type1,arg1,type2,arg2) \ - type LSS_NAME(name)(type1 arg1,type2 arg2) { \ - LSS_BODY(type, \ - : "=a" (__res) \ - : "0" (__NR_##name),"ri" ((long)(arg1)), "c" ((long)(arg2))); \ - } - #undef _syscall3 - #define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ - type LSS_NAME(name)(type1 arg1,type2 arg2,type3 arg3) { \ - LSS_BODY(type, \ - : "=a" (__res) \ - : "0" (__NR_##name), "ri" ((long)(arg1)), "c" ((long)(arg2)), \ - "d" ((long)(arg3))); \ - } - #undef _syscall4 - #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ - type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ - LSS_BODY(type, \ - : "=a" (__res) \ - : "0" (__NR_##name), "ri" ((long)(arg1)), "c" ((long)(arg2)), \ - "d" ((long)(arg3)),"S" ((long)(arg4))); \ - } - #undef _syscall5 - #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ - type5,arg5) \ - type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ - type5 arg5) { \ - long __res; \ - __asm__ __volatile__("push %%ebx\n" \ - "movl %2,%%ebx\n" \ - "movl %1,%%eax\n" \ - "int $0x80\n" \ - "pop %%ebx" \ - : "=a" (__res) \ - : "i" (__NR_##name), "ri" ((long)(arg1)), \ - "c" ((long)(arg2)), "d" ((long)(arg3)), \ - "S" ((long)(arg4)), "D" ((long)(arg5)) \ - : "memory"); \ - LSS_RETURN(type,__res); \ - } - #undef _syscall6 - #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ - type5,arg5,type6,arg6) \ - type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ - type5 arg5, type6 arg6) { \ - long __res; \ - struct { long __a1; long __a6; } __s = { (long)arg1, (long) arg6 }; \ - __asm__ __volatile__("push %%ebp\n" \ - "push %%ebx\n" \ - "movl 4(%2),%%ebp\n" \ - "movl 0(%2), %%ebx\n" \ - "movl %1,%%eax\n" \ - "int $0x80\n" \ - "pop %%ebx\n" \ - "pop %%ebp" \ - : "=a" (__res) \ - : "i" (__NR_##name), "0" ((long)(&__s)), \ - "c" ((long)(arg2)), "d" ((long)(arg3)), \ - "S" ((long)(arg4)), "D" ((long)(arg5)) \ - : "memory"); \ - LSS_RETURN(type,__res); \ - } - LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, - int flags, void *arg, int *parent_tidptr, - void *newtls, int *child_tidptr) { - long __res; - __asm__ __volatile__(/* if (fn == NULL) - * return -EINVAL; - */ - "movl %3,%%ecx\n" - "jecxz 1f\n" - - /* if (child_stack == NULL) - * return -EINVAL; - */ - "movl %4,%%ecx\n" - "jecxz 1f\n" - - /* Set up alignment of the child stack: - * child_stack = (child_stack & ~0xF) - 20; - */ - "andl $-16,%%ecx\n" - "subl $20,%%ecx\n" - - /* Push "arg" and "fn" onto the stack that will be - * used by the child. - */ - "movl %6,%%eax\n" - "movl %%eax,4(%%ecx)\n" - "movl %3,%%eax\n" - "movl %%eax,(%%ecx)\n" - - /* %eax = syscall(%eax = __NR_clone, - * %ebx = flags, - * %ecx = child_stack, - * %edx = parent_tidptr, - * %esi = newtls, - * %edi = child_tidptr) - * Also, make sure that %ebx gets preserved as it is - * used in PIC mode. - */ - "movl %8,%%esi\n" - "movl %7,%%edx\n" - "movl %5,%%eax\n" - "movl %9,%%edi\n" - "pushl %%ebx\n" - "movl %%eax,%%ebx\n" - "movl %2,%%eax\n" - "int $0x80\n" - - /* In the parent: restore %ebx - * In the child: move "fn" into %ebx - */ - "popl %%ebx\n" - - /* if (%eax != 0) - * return %eax; - */ - "test %%eax,%%eax\n" - "jnz 1f\n" - - /* In the child, now. Terminate frame pointer chain. - */ - "movl $0,%%ebp\n" - - /* Call "fn". "arg" is already on the stack. - */ - "call *%%ebx\n" - - /* Call _exit(%ebx). Unfortunately older versions - * of gcc restrict the number of arguments that can - * be passed to asm(). So, we need to hard-code the - * system call number. - */ - "movl %%eax,%%ebx\n" - "movl $1,%%eax\n" - "int $0x80\n" - - /* Return to parent. - */ - "1:\n" - : "=a" (__res) - : "0"(-EINVAL), "i"(__NR_clone), - "m"(fn), "m"(child_stack), "m"(flags), "m"(arg), - "m"(parent_tidptr), "m"(newtls), "m"(child_tidptr) - : "memory", "ecx", "edx", "esi", "edi"); - LSS_RETURN(int, __res); - } - - #define __NR__fadvise64_64 __NR_fadvise64_64 - LSS_INLINE _syscall6(int, _fadvise64_64, int, fd, - unsigned, offset_lo, unsigned, offset_hi, - unsigned, len_lo, unsigned, len_hi, - int, advice) - - LSS_INLINE int LSS_NAME(fadvise64)(int fd, loff_t offset, - loff_t len, int advice) { - return LSS_NAME(_fadvise64_64)(fd, - (unsigned)offset, (unsigned)(offset >>32), - (unsigned)len, (unsigned)(len >> 32), - advice); - } - - LSS_INLINE void (*LSS_NAME(restore_rt)(void))(void) { - /* On i386, the kernel does not know how to return from a signal - * handler. Instead, it relies on user space to provide a - * restorer function that calls the {rt_,}sigreturn() system call. - * Unfortunately, we cannot just reference the glibc version of this - * function, as glibc goes out of its way to make it inaccessible. - */ - void (*res)(void); - __asm__ __volatile__("call 2f\n" - "0:.align 16\n" - "1:movl %1,%%eax\n" - "int $0x80\n" - "2:popl %0\n" - "addl $(1b-0b),%0\n" - : "=a" (res) - : "i" (__NR_rt_sigreturn)); - return res; - } - LSS_INLINE void (*LSS_NAME(restore)(void))(void) { - /* On i386, the kernel does not know how to return from a signal - * handler. Instead, it relies on user space to provide a - * restorer function that calls the {rt_,}sigreturn() system call. - * Unfortunately, we cannot just reference the glibc version of this - * function, as glibc goes out of its way to make it inaccessible. - */ - void (*res)(void); - __asm__ __volatile__("call 2f\n" - "0:.align 16\n" - "1:pop %%eax\n" - "movl %1,%%eax\n" - "int $0x80\n" - "2:popl %0\n" - "addl $(1b-0b),%0\n" - : "=a" (res) - : "i" (__NR_sigreturn)); - return res; - } - #elif defined(__x86_64__) - /* There are no known problems with any of the _syscallX() macros - * currently shipping for x86_64, but we still need to be able to define - * our own version so that we can override the location of the errno - * location (e.g. when using the clone() system call with the CLONE_VM - * option). - */ - #undef LSS_BODY - #define LSS_BODY(type,name, ...) \ - long __res; \ - __asm__ __volatile__("syscall" : "=a" (__res) : "0" (__NR_##name), \ - ##__VA_ARGS__ : "r11", "rcx", "memory"); \ - LSS_RETURN(type, __res) - #undef _syscall0 - #define _syscall0(type,name) \ - type LSS_NAME(name)() { \ - LSS_BODY(type, name); \ - } - #undef _syscall1 - #define _syscall1(type,name,type1,arg1) \ - type LSS_NAME(name)(type1 arg1) { \ - LSS_BODY(type, name, "D" ((long)(arg1))); \ - } - #undef _syscall2 - #define _syscall2(type,name,type1,arg1,type2,arg2) \ - type LSS_NAME(name)(type1 arg1, type2 arg2) { \ - LSS_BODY(type, name, "D" ((long)(arg1)), "S" ((long)(arg2))); \ - } - #undef _syscall3 - #define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ - type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ - LSS_BODY(type, name, "D" ((long)(arg1)), "S" ((long)(arg2)), \ - "d" ((long)(arg3))); \ - } - #undef _syscall4 - #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ - type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ - long __res; \ - __asm__ __volatile__("movq %5,%%r10; syscall" : \ - "=a" (__res) : "0" (__NR_##name), \ - "D" ((long)(arg1)), "S" ((long)(arg2)), "d" ((long)(arg3)), \ - "g" ((long)(arg4)) : "r10", "r11", "rcx", "memory"); \ - LSS_RETURN(type, __res); \ - } - #undef _syscall5 - #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ - type5,arg5) \ - type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ - type5 arg5) { \ - long __res; \ - __asm__ __volatile__("movq %5,%%r10; movq %6,%%r8; syscall" : \ - "=a" (__res) : "0" (__NR_##name), \ - "D" ((long)(arg1)), "S" ((long)(arg2)), "d" ((long)(arg3)), \ - "g" ((long)(arg4)), "g" ((long)(arg5)) : \ - "r8", "r10", "r11", "rcx", "memory"); \ - LSS_RETURN(type, __res); \ - } - #undef _syscall6 - #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ - type5,arg5,type6,arg6) \ - type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ - type5 arg5, type6 arg6) { \ - long __res; \ - __asm__ __volatile__("movq %5,%%r10; movq %6,%%r8; movq %7,%%r9;" \ - "syscall" : \ - "=a" (__res) : "0" (__NR_##name), \ - "D" ((long)(arg1)), "S" ((long)(arg2)), "d" ((long)(arg3)), \ - "g" ((long)(arg4)), "g" ((long)(arg5)), "g" ((long)(arg6)) : \ - "r8", "r9", "r10", "r11", "rcx", "memory"); \ - LSS_RETURN(type, __res); \ - } - LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, - int flags, void *arg, int *parent_tidptr, - void *newtls, int *child_tidptr) { - long __res; - { - register void *__tls __asm__("r8") = newtls; - register int *__ctid __asm__("r10") = child_tidptr; - __asm__ __volatile__(/* if (fn == NULL) - * return -EINVAL; - */ - "testq %4,%4\n" - "jz 1f\n" - - /* if (child_stack == NULL) - * return -EINVAL; - */ - "testq %5,%5\n" - "jz 1f\n" - - /* childstack -= 2*sizeof(void *); - */ - "subq $16,%5\n" - - /* Push "arg" and "fn" onto the stack that will be - * used by the child. - */ - "movq %7,8(%5)\n" - "movq %4,0(%5)\n" - - /* %rax = syscall(%rax = __NR_clone, - * %rdi = flags, - * %rsi = child_stack, - * %rdx = parent_tidptr, - * %r8 = new_tls, - * %r10 = child_tidptr) - */ - "movq %2,%%rax\n" - "syscall\n" - - /* if (%rax != 0) - * return; - */ - "testq %%rax,%%rax\n" - "jnz 1f\n" - - /* In the child. Terminate frame pointer chain. - */ - "xorq %%rbp,%%rbp\n" - - /* Call "fn(arg)". - */ - "popq %%rax\n" - "popq %%rdi\n" - "call *%%rax\n" - - /* Call _exit(%ebx). - */ - "movq %%rax,%%rdi\n" - "movq %3,%%rax\n" - "syscall\n" - - /* Return to parent. - */ - "1:\n" - : "=a" (__res) - : "0"(-EINVAL), "i"(__NR_clone), "i"(__NR_exit), - "r"(fn), "S"(child_stack), "D"(flags), "r"(arg), - "d"(parent_tidptr), "r"(__tls), "r"(__ctid) - : "memory", "r11", "rcx"); - } - LSS_RETURN(int, __res); - } - LSS_INLINE _syscall4(int, fadvise64, int, fd, loff_t, offset, loff_t, len, - int, advice) - - LSS_INLINE void (*LSS_NAME(restore_rt)(void))(void) { - /* On x86-64, the kernel does not know how to return from - * a signal handler. Instead, it relies on user space to provide a - * restorer function that calls the rt_sigreturn() system call. - * Unfortunately, we cannot just reference the glibc version of this - * function, as glibc goes out of its way to make it inaccessible. - */ - void (*res)(void); - __asm__ __volatile__("call 2f\n" - "0:.align 16\n" - "1:movq %1,%%rax\n" - "syscall\n" - "2:popq %0\n" - "addq $(1b-0b),%0\n" - : "=a" (res) - : "i" (__NR_rt_sigreturn)); - return res; - } - #elif defined(__ARM_ARCH_3__) - /* Most definitions of _syscallX() neglect to mark "memory" as being - * clobbered. This causes problems with compilers, that do a better job - * at optimizing across __asm__ calls. - * So, we just have to redefine all fo the _syscallX() macros. - */ - #undef LSS_REG - #define LSS_REG(r,a) register long __r##r __asm__("r"#r) = (long)a - #undef LSS_BODY - #define LSS_BODY(type,name,args...) \ - register long __res_r0 __asm__("r0"); \ - long __res; \ - __asm__ __volatile__ (__syscall(name) \ - : "=r"(__res_r0) : args : "lr", "memory"); \ - __res = __res_r0; \ - LSS_RETURN(type, __res) - #undef _syscall0 - #define _syscall0(type, name) \ - type LSS_NAME(name)() { \ - LSS_BODY(type, name); \ - } - #undef _syscall1 - #define _syscall1(type, name, type1, arg1) \ - type LSS_NAME(name)(type1 arg1) { \ - LSS_REG(0, arg1); LSS_BODY(type, name, "r"(__r0)); \ - } - #undef _syscall2 - #define _syscall2(type, name, type1, arg1, type2, arg2) \ - type LSS_NAME(name)(type1 arg1, type2 arg2) { \ - LSS_REG(0, arg1); LSS_REG(1, arg2); \ - LSS_BODY(type, name, "r"(__r0), "r"(__r1)); \ - } - #undef _syscall3 - #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ - type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ - LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ - LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2)); \ - } - #undef _syscall4 - #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ - type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ - LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ - LSS_REG(3, arg4); \ - LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3)); \ - } - #undef _syscall5 - #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ - type5,arg5) \ - type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ - type5 arg5) { \ - LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ - LSS_REG(3, arg4); LSS_REG(4, arg5); \ - LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ - "r"(__r4)); \ - } - #undef _syscall6 - #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ - type5,arg5,type6,arg6) \ - type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ - type5 arg5, type6 arg6) { \ - LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ - LSS_REG(3, arg4); LSS_REG(4, arg5); LSS_REG(5, arg6); \ - LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ - "r"(__r4), "r"(__r5)); \ - } - LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, - int flags, void *arg, int *parent_tidptr, - void *newtls, int *child_tidptr) { - long __res; - { - register int __flags __asm__("r0") = flags; - register void *__stack __asm__("r1") = child_stack; - register void *__ptid __asm__("r2") = parent_tidptr; - register void *__tls __asm__("r3") = newtls; - register int *__ctid __asm__("r4") = child_tidptr; - __asm__ __volatile__(/* if (fn == NULL || child_stack == NULL) - * return -EINVAL; - */ - "cmp %2,#0\n" - "cmpne %3,#0\n" - "moveq %0,%1\n" - "beq 1f\n" - - /* Push "arg" and "fn" onto the stack that will be - * used by the child. - */ - "str %5,[%3,#-4]!\n" - "str %2,[%3,#-4]!\n" - - /* %r0 = syscall(%r0 = flags, - * %r1 = child_stack, - * %r2 = parent_tidptr, - * %r3 = newtls, - * %r4 = child_tidptr) - */ - __syscall(clone)"\n" - - /* if (%r0 != 0) - * return %r0; - */ - "movs %0,r0\n" - "bne 1f\n" - - /* In the child, now. Call "fn(arg)". - */ - "ldr r0,[sp, #4]\n" - "mov lr,pc\n" - "ldr pc,[sp]\n" - - /* Call _exit(%r0). - */ - __syscall(exit)"\n" - "1:\n" - : "=r" (__res) - : "i"(-EINVAL), - "r"(fn), "r"(__stack), "r"(__flags), "r"(arg), - "r"(__ptid), "r"(__tls), "r"(__ctid) - : "lr", "memory"); - } - LSS_RETURN(int, __res); - } - #elif defined(__mips__) - #undef LSS_REG - #define LSS_REG(r,a) register unsigned long __r##r __asm__("$"#r) = \ - (unsigned long)(a) - #undef LSS_BODY - #define LSS_BODY(type,name,r7,...) \ - register unsigned long __v0 __asm__("$2") = __NR_##name; \ - __asm__ __volatile__ ("syscall\n" \ - : "=&r"(__v0), r7 (__r7) \ - : "0"(__v0), ##__VA_ARGS__ \ - : "$8", "$9", "$10", "$11", "$12", \ - "$13", "$14", "$15", "$24", "memory"); \ - LSS_RETURN(type, __v0, __r7) - #undef _syscall0 - #define _syscall0(type, name) \ - type LSS_NAME(name)() { \ - register unsigned long __r7 __asm__("$7"); \ - LSS_BODY(type, name, "=r"); \ - } - #undef _syscall1 - #define _syscall1(type, name, type1, arg1) \ - type LSS_NAME(name)(type1 arg1) { \ - register unsigned long __r7 __asm__("$7"); \ - LSS_REG(4, arg1); LSS_BODY(type, name, "=r", "r"(__r4)); \ - } - #undef _syscall2 - #define _syscall2(type, name, type1, arg1, type2, arg2) \ - type LSS_NAME(name)(type1 arg1, type2 arg2) { \ - register unsigned long __r7 __asm__("$7"); \ - LSS_REG(4, arg1); LSS_REG(5, arg2); \ - LSS_BODY(type, name, "=r", "r"(__r4), "r"(__r5)); \ - } - #undef _syscall3 - #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ - type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ - register unsigned long __r7 __asm__("$7"); \ - LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ - LSS_BODY(type, name, "=r", "r"(__r4), "r"(__r5), "r"(__r6)); \ - } - #undef _syscall4 - #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ - type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ - LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ - LSS_REG(7, arg4); \ - LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6)); \ - } - #undef _syscall5 - #if _MIPS_SIM == _MIPS_SIM_ABI32 - /* The old 32bit MIPS system call API passes the fifth and sixth argument - * on the stack, whereas the new APIs use registers "r8" and "r9". - */ - #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ - type5,arg5) \ - type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ - type5 arg5) { \ - LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ - LSS_REG(7, arg4); \ - register unsigned long __v0 __asm__("$2"); \ - __asm__ __volatile__ (".set noreorder\n" \ - "lw $2, %6\n" \ - "subu $29, 32\n" \ - "sw $2, 16($29)\n" \ - "li $2, %2\n" \ - "syscall\n" \ - "addiu $29, 32\n" \ - ".set reorder\n" \ - : "=&r"(__v0), "+r" (__r7) \ - : "i" (__NR_##name), "r"(__r4), "r"(__r5), \ - "r"(__r6), "m" ((unsigned long)arg5) \ - : "$8", "$9", "$10", "$11", "$12", \ - "$13", "$14", "$15", "$24", "memory"); \ - LSS_RETURN(type, __v0, __r7); \ - } - #else - #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ - type5,arg5) \ - type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ - type5 arg5) { \ - LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ - LSS_REG(7, arg4); LSS_REG(8, arg5); \ - LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6), \ - "r"(__r8)); \ - } - #endif - #undef _syscall6 - #if _MIPS_SIM == _MIPS_SIM_ABI32 - /* The old 32bit MIPS system call API passes the fifth and sixth argument - * on the stack, whereas the new APIs use registers "r8" and "r9". - */ - #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ - type5,arg5,type6,arg6) \ - type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ - type5 arg5, type6 arg6) { \ - LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ - LSS_REG(7, arg4); \ - register unsigned long __v0 __asm__("$2"); \ - __asm__ __volatile__ (".set noreorder\n" \ - "lw $2, %6\n" \ - "lw $8, %7\n" \ - "subu $29, 32\n" \ - "sw $2, 16($29)\n" \ - "sw $8, 20($29)\n" \ - "li $2, %2\n" \ - "syscall\n" \ - "addiu $29, 32\n" \ - ".set reorder\n" \ - : "=&r"(__v0), "+r" (__r7) \ - : "i" (__NR_##name), "r"(__r4), "r"(__r5), \ - "r"(__r6), "r" ((unsigned long)arg5), \ - "r" ((unsigned long)arg6) \ - : "$8", "$9", "$10", "$11", "$12", \ - "$13", "$14", "$15", "$24", "memory"); \ - LSS_RETURN(type, __v0, __r7); \ - } - #else - #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ - type5,arg5,type6,arg6) \ - type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ - type5 arg5,type6 arg6) { \ - LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ - LSS_REG(7, arg4); LSS_REG(8, arg5); LSS_REG(9, arg6); \ - LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6), \ - "r"(__r8), "r"(__r9)); \ - } - #endif - LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, - int flags, void *arg, int *parent_tidptr, - void *newtls, int *child_tidptr) { - register unsigned long __v0 __asm__("$2"); - register unsigned long __r7 __asm__("$7") = (unsigned long)newtls; - { - register int __flags __asm__("$4") = flags; - register void *__stack __asm__("$5") = child_stack; - register void *__ptid __asm__("$6") = parent_tidptr; - register int *__ctid __asm__("$8") = child_tidptr; - __asm__ __volatile__( - #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 - "subu $29,24\n" - #elif _MIPS_SIM == _MIPS_SIM_NABI32 - "sub $29,16\n" - #else - "dsubu $29,16\n" - #endif - - /* if (fn == NULL || child_stack == NULL) - * return -EINVAL; - */ - "li %0,%2\n" - "beqz %5,1f\n" - "beqz %6,1f\n" - - /* Push "arg" and "fn" onto the stack that will be - * used by the child. - */ - #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 - "subu %6,32\n" - "sw %5,0(%6)\n" - "sw %8,4(%6)\n" - #elif _MIPS_SIM == _MIPS_SIM_NABI32 - "sub %6,32\n" - "sw %5,0(%6)\n" - "sw %8,8(%6)\n" - #else - "dsubu %6,32\n" - "sd %5,0(%6)\n" - "sd %8,8(%6)\n" - #endif - - /* $7 = syscall($4 = flags, - * $5 = child_stack, - * $6 = parent_tidptr, - * $7 = newtls, - * $8 = child_tidptr) - */ - "li $2,%3\n" - "syscall\n" - - /* if ($7 != 0) - * return $2; - */ - "bnez $7,1f\n" - "bnez $2,1f\n" - - /* In the child, now. Call "fn(arg)". - */ - #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 - "lw $25,0($29)\n" - "lw $4,4($29)\n" - #elif _MIPS_SIM == _MIPS_SIM_NABI32 - "lw $25,0($29)\n" - "lw $4,8($29)\n" - #else - "ld $25,0($29)\n" - "ld $4,8($29)\n" - #endif - "jalr $25\n" - - /* Call _exit($2) - */ - "move $4,$2\n" - "li $2,%4\n" - "syscall\n" - - "1:\n" - #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 - "addu $29, 24\n" - #elif _MIPS_SIM == _MIPS_SIM_NABI32 - "add $29, 16\n" - #else - "daddu $29,16\n" - #endif - : "=&r" (__v0), "=r" (__r7) - : "i"(-EINVAL), "i"(__NR_clone), "i"(__NR_exit), - "r"(fn), "r"(__stack), "r"(__flags), "r"(arg), - "r"(__ptid), "r"(__r7), "r"(__ctid) - : "$9", "$10", "$11", "$12", "$13", "$14", "$15", - "$24", "memory"); - } - LSS_RETURN(int, __v0, __r7); - } - #elif defined (__PPC__) - #undef LSS_LOADARGS_0 - #define LSS_LOADARGS_0(name, dummy...) \ - __sc_0 = __NR_##name - #undef LSS_LOADARGS_1 - #define LSS_LOADARGS_1(name, arg1) \ - LSS_LOADARGS_0(name); \ - __sc_3 = (unsigned long) (arg1) - #undef LSS_LOADARGS_2 - #define LSS_LOADARGS_2(name, arg1, arg2) \ - LSS_LOADARGS_1(name, arg1); \ - __sc_4 = (unsigned long) (arg2) - #undef LSS_LOADARGS_3 - #define LSS_LOADARGS_3(name, arg1, arg2, arg3) \ - LSS_LOADARGS_2(name, arg1, arg2); \ - __sc_5 = (unsigned long) (arg3) - #undef LSS_LOADARGS_4 - #define LSS_LOADARGS_4(name, arg1, arg2, arg3, arg4) \ - LSS_LOADARGS_3(name, arg1, arg2, arg3); \ - __sc_6 = (unsigned long) (arg4) - #undef LSS_LOADARGS_5 - #define LSS_LOADARGS_5(name, arg1, arg2, arg3, arg4, arg5) \ - LSS_LOADARGS_4(name, arg1, arg2, arg3, arg4); \ - __sc_7 = (unsigned long) (arg5) - #undef LSS_LOADARGS_6 - #define LSS_LOADARGS_6(name, arg1, arg2, arg3, arg4, arg5, arg6) \ - LSS_LOADARGS_5(name, arg1, arg2, arg3, arg4, arg5); \ - __sc_8 = (unsigned long) (arg6) - #undef LSS_ASMINPUT_0 - #define LSS_ASMINPUT_0 "0" (__sc_0) - #undef LSS_ASMINPUT_1 - #define LSS_ASMINPUT_1 LSS_ASMINPUT_0, "1" (__sc_3) - #undef LSS_ASMINPUT_2 - #define LSS_ASMINPUT_2 LSS_ASMINPUT_1, "2" (__sc_4) - #undef LSS_ASMINPUT_3 - #define LSS_ASMINPUT_3 LSS_ASMINPUT_2, "3" (__sc_5) - #undef LSS_ASMINPUT_4 - #define LSS_ASMINPUT_4 LSS_ASMINPUT_3, "4" (__sc_6) - #undef LSS_ASMINPUT_5 - #define LSS_ASMINPUT_5 LSS_ASMINPUT_4, "5" (__sc_7) - #undef LSS_ASMINPUT_6 - #define LSS_ASMINPUT_6 LSS_ASMINPUT_5, "6" (__sc_8) - #undef LSS_BODY - #define LSS_BODY(nr, type, name, args...) \ - long __sc_ret, __sc_err; \ - { \ - register unsigned long __sc_0 __asm__ ("r0"); \ - register unsigned long __sc_3 __asm__ ("r3"); \ - register unsigned long __sc_4 __asm__ ("r4"); \ - register unsigned long __sc_5 __asm__ ("r5"); \ - register unsigned long __sc_6 __asm__ ("r6"); \ - register unsigned long __sc_7 __asm__ ("r7"); \ - register unsigned long __sc_8 __asm__ ("r8"); \ - \ - LSS_LOADARGS_##nr(name, args); \ - __asm__ __volatile__ \ - ("sc\n\t" \ - "mfcr %0" \ - : "=&r" (__sc_0), \ - "=&r" (__sc_3), "=&r" (__sc_4), \ - "=&r" (__sc_5), "=&r" (__sc_6), \ - "=&r" (__sc_7), "=&r" (__sc_8) \ - : LSS_ASMINPUT_##nr \ - : "cr0", "ctr", "memory", \ - "r9", "r10", "r11", "r12"); \ - __sc_ret = __sc_3; \ - __sc_err = __sc_0; \ - } \ - LSS_RETURN(type, __sc_ret, __sc_err) - #undef _syscall0 - #define _syscall0(type, name) \ - type LSS_NAME(name)(void) { \ - LSS_BODY(0, type, name); \ - } - #undef _syscall1 - #define _syscall1(type, name, type1, arg1) \ - type LSS_NAME(name)(type1 arg1) { \ - LSS_BODY(1, type, name, arg1); \ - } - #undef _syscall2 - #define _syscall2(type, name, type1, arg1, type2, arg2) \ - type LSS_NAME(name)(type1 arg1, type2 arg2) { \ - LSS_BODY(2, type, name, arg1, arg2); \ - } - #undef _syscall3 - #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ - type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ - LSS_BODY(3, type, name, arg1, arg2, arg3); \ - } - #undef _syscall4 - #define _syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, \ - type4, arg4) \ - type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ - LSS_BODY(4, type, name, arg1, arg2, arg3, arg4); \ - } - #undef _syscall5 - #define _syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, \ - type4, arg4, type5, arg5) \ - type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ - type5 arg5) { \ - LSS_BODY(5, type, name, arg1, arg2, arg3, arg4, arg5); \ - } - #undef _syscall6 - #define _syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, \ - type4, arg4, type5, arg5, type6, arg6) \ - type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ - type5 arg5, type6 arg6) { \ - LSS_BODY(6, type, name, arg1, arg2, arg3, arg4, arg5, arg6); \ - } - /* clone function adapted from glibc 2.3.6 clone.S */ - /* TODO(csilvers): consider wrapping some args up in a struct, like we - * do for i386's _syscall6, so we can compile successfully on gcc 2.95 - */ - LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, - int flags, void *arg, int *parent_tidptr, - void *newtls, int *child_tidptr) { - long __ret, __err; - { - register int (*__fn)(void *) __asm__ ("r8") = fn; - register void *__cstack __asm__ ("r4") = child_stack; - register int __flags __asm__ ("r3") = flags; - register void * __arg __asm__ ("r9") = arg; - register int * __ptidptr __asm__ ("r5") = parent_tidptr; - register void * __newtls __asm__ ("r6") = newtls; - register int * __ctidptr __asm__ ("r7") = child_tidptr; - __asm__ __volatile__( - /* check for fn == NULL - * and child_stack == NULL - */ - "cmpwi cr0, %6, 0\n\t" - "cmpwi cr1, %7, 0\n\t" - "cror cr0*4+eq, cr1*4+eq, cr0*4+eq\n\t" - "beq- cr0, 1f\n\t" - - /* set up stack frame for child */ - "clrrwi %7, %7, 4\n\t" - "li 0, 0\n\t" - "stwu 0, -16(%7)\n\t" - - /* fn, arg, child_stack are saved across the syscall: r28-30 */ - "mr 28, %6\n\t" - "mr 29, %7\n\t" - "mr 27, %9\n\t" - - /* syscall */ - "li 0, %4\n\t" - /* flags already in r3 - * child_stack already in r4 - * ptidptr already in r5 - * newtls already in r6 - * ctidptr already in r7 - */ - "sc\n\t" - - /* Test if syscall was successful */ - "cmpwi cr1, 3, 0\n\t" - "crandc cr1*4+eq, cr1*4+eq, cr0*4+so\n\t" - "bne- cr1, 1f\n\t" - - /* Do the function call */ - "mtctr 28\n\t" - "mr 3, 27\n\t" - "bctrl\n\t" - - /* Call _exit(r3) */ - "li 0, %5\n\t" - "sc\n\t" - - /* Return to parent */ - "1:\n" - "mfcr %1\n\t" - "mr %0, 3\n\t" - : "=r" (__ret), "=r" (__err) - : "0" (-1), "1" (EINVAL), - "i" (__NR_clone), "i" (__NR_exit), - "r" (__fn), "r" (__cstack), "r" (__flags), - "r" (__arg), "r" (__ptidptr), "r" (__newtls), - "r" (__ctidptr) - : "cr0", "cr1", "memory", "ctr", - "r0", "r29", "r27", "r28"); - } - LSS_RETURN(int, __ret, __err); - } - #endif - #define __NR__exit __NR_exit - #define __NR__gettid __NR_gettid - #define __NR__mremap __NR_mremap - LSS_INLINE _syscall1(int, chdir, const char *,p) - LSS_INLINE _syscall1(int, close, int, f) - LSS_INLINE _syscall1(int, dup, int, f) - LSS_INLINE _syscall2(int, dup2, int, s, - int, d) - LSS_INLINE _syscall3(int, execve, const char*, f, - const char*const*,a,const char*const*, e) - LSS_INLINE _syscall1(int, _exit, int, e) - LSS_INLINE _syscall3(int, fcntl, int, f, - int, c, long, a) - LSS_INLINE _syscall0(pid_t, fork) - LSS_INLINE _syscall2(int, fstat, int, f, - struct kernel_stat*, b) - LSS_INLINE _syscall2(int, fstatfs, int, f, - struct kernel_statfs*, b) - LSS_INLINE _syscall4(int, futex, int*, a, - int, o, int, v, - struct kernel_timespec*, t) - LSS_INLINE _syscall3(int, getdents, int, f, - struct kernel_dirent*, d, int, c) - LSS_INLINE _syscall3(int, getdents64, int, f, - struct kernel_dirent64*, d, int, c) - LSS_INLINE _syscall0(gid_t, getegid) - LSS_INLINE _syscall0(uid_t, geteuid) - LSS_INLINE _syscall0(pid_t, getpgrp) - LSS_INLINE _syscall0(pid_t, getpid) - LSS_INLINE _syscall0(pid_t, getppid) - LSS_INLINE _syscall2(int, getpriority, int, a, - int, b) - LSS_INLINE _syscall2(int, getrlimit, int, r, - struct kernel_rlimit*, l) - LSS_INLINE _syscall1(pid_t, getsid, pid_t, p) - LSS_INLINE _syscall0(pid_t, _gettid) - LSS_INLINE _syscall5(int, setxattr, const char *,p, - const char *, n, const void *,v, - size_t, s, int, f) - LSS_INLINE _syscall5(int, lsetxattr, const char *,p, - const char *, n, const void *,v, - size_t, s, int, f) - LSS_INLINE _syscall4(ssize_t, getxattr, const char *,p, - const char *, n, void *, v, size_t, s) - LSS_INLINE _syscall4(ssize_t, lgetxattr, const char *,p, - const char *, n, void *, v, size_t, s) - LSS_INLINE _syscall2(int, kill, pid_t, p, - int, s) - LSS_INLINE _syscall3(off_t, lseek, int, f, - off_t, o, int, w) - LSS_INLINE _syscall2(int, munmap, void*, s, - size_t, l) - LSS_INLINE _syscall6(long, move_pages, pid_t, p, - unsigned long, n, void **,g, int *, d, - int *, s, int, f) - LSS_INLINE _syscall5(void*, _mremap, void*, o, - size_t, os, size_t, ns, - unsigned long, f, void *, a) - LSS_INLINE _syscall3(int, open, const char*, p, - int, f, int, m) - LSS_INLINE _syscall3(int, poll, struct kernel_pollfd*, u, - unsigned int, n, int, t) - LSS_INLINE _syscall2(int, prctl, int, o, - long, a) - LSS_INLINE _syscall4(long, ptrace, int, r, - pid_t, p, void *, a, void *, d) - LSS_INLINE _syscall3(ssize_t, read, int, f, - void *, b, size_t, c) - LSS_INLINE _syscall3(int, readlink, const char*, p, - char*, b, size_t, s) - LSS_INLINE _syscall4(int, rt_sigaction, int, s, - const struct kernel_sigaction*, a, - struct kernel_sigaction*, o, size_t, c) - LSS_INLINE _syscall2(int, rt_sigpending, struct kernel_sigset_t *, s, - size_t, c) - LSS_INLINE _syscall4(int, rt_sigprocmask, int, h, - const struct kernel_sigset_t*, s, - struct kernel_sigset_t*, o, size_t, c) - LSS_INLINE _syscall2(int, rt_sigsuspend, - const struct kernel_sigset_t*, s, size_t, c) - LSS_INLINE _syscall3(int, sched_getaffinity,pid_t, p, - unsigned int, l, unsigned long *, m) - LSS_INLINE _syscall3(int, sched_setaffinity,pid_t, p, - unsigned int, l, unsigned long *, m) - LSS_INLINE _syscall0(int, sched_yield) - LSS_INLINE _syscall1(long, set_tid_address, int *, t) - LSS_INLINE _syscall1(int, setfsgid, gid_t, g) - LSS_INLINE _syscall1(int, setfsuid, uid_t, u) - LSS_INLINE _syscall2(int, setpgid, pid_t, p, - pid_t, g) - LSS_INLINE _syscall3(int, setpriority, int, a, - int, b, int, p) - LSS_INLINE _syscall3(int, setresgid, gid_t, r, - gid_t, e, gid_t, s) - LSS_INLINE _syscall3(int, setresuid, uid_t, r, - uid_t, e, uid_t, s) - LSS_INLINE _syscall2(int, setrlimit, int, r, - const struct kernel_rlimit*, l) - LSS_INLINE _syscall0(pid_t, setsid) - LSS_INLINE _syscall2(int, sigaltstack, const stack_t*, s, - const stack_t*, o) - LSS_INLINE _syscall2(int, stat, const char*, f, - struct kernel_stat*, b) - LSS_INLINE _syscall2(int, statfs, const char*, f, - struct kernel_statfs*, b) - LSS_INLINE _syscall1(int, unlink, const char*, f) - LSS_INLINE _syscall3(ssize_t, write, int, f, - const void *, b, size_t, c) - LSS_INLINE _syscall3(ssize_t, writev, int, f, - const struct kernel_iovec*, v, size_t, c) - #if defined(__x86_64__) || \ - (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI32) - LSS_INLINE _syscall3(int, recvmsg, int, s, - struct kernel_msghdr*, m, int, f) - LSS_INLINE _syscall3(int, sendmsg, int, s, - const struct kernel_msghdr*, m, int, f) - LSS_INLINE _syscall6(int, sendto, int, s, - const void*, m, size_t, l, - int, f, - const struct kernel_sockaddr*, a, int, t) - LSS_INLINE _syscall2(int, shutdown, int, s, - int, h) - LSS_INLINE _syscall3(int, socket, int, d, - int, t, int, p) - LSS_INLINE _syscall4(int, socketpair, int, d, - int, t, int, p, int*, s) - #endif - #if defined(__x86_64__) - LSS_INLINE _syscall6(void*, mmap, void*, s, - size_t, l, int, p, - int, f, int, d, - __off64_t, o) - LSS_INLINE _syscall4(int, newfstatat, int, d, - const char *, p, - struct kernel_stat*, b, int, f) - - LSS_INLINE int LSS_NAME(setfsgid32)(gid_t gid) { - return LSS_NAME(setfsgid)(gid); - } - - LSS_INLINE int LSS_NAME(setfsuid32)(uid_t uid) { - return LSS_NAME(setfsuid)(uid); - } - - LSS_INLINE int LSS_NAME(setresgid32)(gid_t rgid, gid_t egid, gid_t sgid) { - return LSS_NAME(setresgid)(rgid, egid, sgid); - } - - LSS_INLINE int LSS_NAME(setresuid32)(uid_t ruid, uid_t euid, uid_t suid) { - return LSS_NAME(setresuid)(ruid, euid, suid); - } - - LSS_INLINE int LSS_NAME(sigaction)(int signum, - const struct kernel_sigaction *act, - struct kernel_sigaction *oldact) { - /* On x86_64, the kernel requires us to always set our own - * SA_RESTORER in order to be able to return from a signal handler. - * This function must have a "magic" signature that the "gdb" - * (and maybe the kernel?) can recognize. - */ - if (act != NULL && !(act->sa_flags & SA_RESTORER)) { - struct kernel_sigaction a = *act; - a.sa_flags |= SA_RESTORER; - a.sa_restorer = LSS_NAME(restore_rt)(); - return LSS_NAME(rt_sigaction)(signum, &a, oldact, - (KERNEL_NSIG+7)/8); - } else { - return LSS_NAME(rt_sigaction)(signum, act, oldact, - (KERNEL_NSIG+7)/8); - } - } - - LSS_INLINE int LSS_NAME(sigpending)(struct kernel_sigset_t *set) { - return LSS_NAME(rt_sigpending)(set, (KERNEL_NSIG+7)/8); - } - - LSS_INLINE int LSS_NAME(sigprocmask)(int how, - const struct kernel_sigset_t *set, - struct kernel_sigset_t *oldset) { - return LSS_NAME(rt_sigprocmask)(how, set, oldset, (KERNEL_NSIG+7)/8); - } - - LSS_INLINE int LSS_NAME(sigsuspend)(const struct kernel_sigset_t *set) { - return LSS_NAME(rt_sigsuspend)(set, (KERNEL_NSIG+7)/8); - } - #endif - #if defined(__x86_64__) || defined(__ARM_ARCH_3__) || \ - (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI32) - LSS_INLINE _syscall4(pid_t, wait4, pid_t, p, - int*, s, int, o, - struct kernel_rusage*, r) - - LSS_INLINE pid_t LSS_NAME(waitpid)(pid_t pid, int *status, int options){ - return LSS_NAME(wait4)(pid, status, options, 0); - } - #endif - #if defined(__i386__) || defined(__x86_64__) - LSS_INLINE _syscall4(int, openat, int, d, const char *, p, int, f, int, m) - LSS_INLINE _syscall3(int, unlinkat, int, d, const char *, p, int, f) - #endif - #if defined(__i386__) || defined(__ARM_ARCH_3__) - #define __NR__setfsgid32 __NR_setfsgid32 - #define __NR__setfsuid32 __NR_setfsuid32 - #define __NR__setresgid32 __NR_setresgid32 - #define __NR__setresuid32 __NR_setresuid32 - LSS_INLINE _syscall2(int, ugetrlimit, int, r, - struct kernel_rlimit*, l) - LSS_INLINE _syscall1(int, _setfsgid32, gid_t, f) - LSS_INLINE _syscall1(int, _setfsuid32, uid_t, f) - LSS_INLINE _syscall3(int, _setresgid32, gid_t, r, - gid_t, e, gid_t, s) - LSS_INLINE _syscall3(int, _setresuid32, uid_t, r, - uid_t, e, uid_t, s) - - LSS_INLINE int LSS_NAME(setfsgid32)(gid_t gid) { - int rc; - if ((rc = LSS_NAME(_setfsgid32)(gid)) < 0 && - LSS_ERRNO == ENOSYS) { - if ((unsigned int)gid & ~0xFFFFu) { - rc = EINVAL; - } else { - rc = LSS_NAME(setfsgid)(gid); - } - } - return rc; - } - - LSS_INLINE int LSS_NAME(setfsuid32)(uid_t uid) { - int rc; - if ((rc = LSS_NAME(_setfsuid32)(uid)) < 0 && - LSS_ERRNO == ENOSYS) { - if ((unsigned int)uid & ~0xFFFFu) { - rc = EINVAL; - } else { - rc = LSS_NAME(setfsuid)(uid); - } - } - return rc; - } - - LSS_INLINE int LSS_NAME(setresgid32)(gid_t rgid, gid_t egid, gid_t sgid) { - int rc; - if ((rc = LSS_NAME(_setresgid32)(rgid, egid, sgid)) < 0 && - LSS_ERRNO == ENOSYS) { - if ((unsigned int)rgid & ~0xFFFFu || - (unsigned int)egid & ~0xFFFFu || - (unsigned int)sgid & ~0xFFFFu) { - rc = EINVAL; - } else { - rc = LSS_NAME(setresgid)(rgid, egid, sgid); - } - } - return rc; - } - - LSS_INLINE int LSS_NAME(setresuid32)(uid_t ruid, uid_t euid, uid_t suid) { - int rc; - if ((rc = LSS_NAME(_setresuid32)(ruid, euid, suid)) < 0 && - LSS_ERRNO == ENOSYS) { - if ((unsigned int)ruid & ~0xFFFFu || - (unsigned int)euid & ~0xFFFFu || - (unsigned int)suid & ~0xFFFFu) { - rc = EINVAL; - } else { - rc = LSS_NAME(setresuid)(ruid, euid, suid); - } - } - return rc; - } - #endif - LSS_INLINE int LSS_NAME(sigemptyset)(struct kernel_sigset_t *set) { - memset(&set->sig, 0, sizeof(set->sig)); - return 0; - } - - LSS_INLINE int LSS_NAME(sigfillset)(struct kernel_sigset_t *set) { - memset(&set->sig, -1, sizeof(set->sig)); - return 0; - } - - LSS_INLINE int LSS_NAME(sigaddset)(struct kernel_sigset_t *set, - int signum) { - if (signum < 1 || signum > (int)(8*sizeof(set->sig))) { - LSS_ERRNO = EINVAL; - return -1; - } else { - set->sig[(signum - 1)/(8*sizeof(set->sig[0]))] - |= 1UL << ((signum - 1) % (8*sizeof(set->sig[0]))); - return 0; - } - } - - LSS_INLINE int LSS_NAME(sigdelset)(struct kernel_sigset_t *set, - int signum) { - if (signum < 1 || signum > (int)(8*sizeof(set->sig))) { - LSS_ERRNO = EINVAL; - return -1; - } else { - set->sig[(signum - 1)/(8*sizeof(set->sig[0]))] - &= ~(1UL << ((signum - 1) % (8*sizeof(set->sig[0])))); - return 0; - } - } - - LSS_INLINE int LSS_NAME(sigismember)(struct kernel_sigset_t *set, - int signum) { - if (signum < 1 || signum > (int)(8*sizeof(set->sig))) { - LSS_ERRNO = EINVAL; - return -1; - } else { - return !!(set->sig[(signum - 1)/(8*sizeof(set->sig[0]))] & - (1UL << ((signum - 1) % (8*sizeof(set->sig[0]))))); - } - } - #if defined(__i386__) || defined(__ARM_ARCH_3__) || \ - (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || defined(__PPC__) - #define __NR__sigaction __NR_sigaction - #define __NR__sigpending __NR_sigpending - #define __NR__sigprocmask __NR_sigprocmask - #define __NR__sigsuspend __NR_sigsuspend - #define __NR__socketcall __NR_socketcall - LSS_INLINE _syscall2(int, fstat64, int, f, - struct kernel_stat64 *, b) - LSS_INLINE _syscall5(int, _llseek, uint, fd, ulong, hi, ulong, lo, - loff_t *, res, uint, wh) - LSS_INLINE _syscall1(void*, mmap, void*, a) - LSS_INLINE _syscall6(void*, mmap2, void*, s, - size_t, l, int, p, - int, f, int, d, - __off64_t, o) - LSS_INLINE _syscall3(int, _sigaction, int, s, - const struct kernel_old_sigaction*, a, - struct kernel_old_sigaction*, o) - LSS_INLINE _syscall1(int, _sigpending, unsigned long*, s) - LSS_INLINE _syscall3(int, _sigprocmask, int, h, - const unsigned long*, s, - unsigned long*, o) - #ifdef __PPC__ - LSS_INLINE _syscall1(int, _sigsuspend, unsigned long, s) - #else - LSS_INLINE _syscall3(int, _sigsuspend, const void*, a, - int, b, - unsigned long, s) - #endif - LSS_INLINE _syscall2(int, stat64, const char *, p, - struct kernel_stat64 *, b) - - LSS_INLINE int LSS_NAME(sigaction)(int signum, - const struct kernel_sigaction *act, - struct kernel_sigaction *oldact) { - int old_errno = LSS_ERRNO; - int rc; - struct kernel_sigaction a; - if (act != NULL) { - a = *act; - #ifdef __i386__ - /* On i386, the kernel requires us to always set our own - * SA_RESTORER when using realtime signals. Otherwise, it does not - * know how to return from a signal handler. This function must have - * a "magic" signature that the "gdb" (and maybe the kernel?) can - * recognize. - * Apparently, a SA_RESTORER is implicitly set by the kernel, when - * using non-realtime signals. - * - * TODO: Test whether ARM needs a restorer - */ - if (!(a.sa_flags & SA_RESTORER)) { - a.sa_flags |= SA_RESTORER; - a.sa_restorer = (a.sa_flags & SA_SIGINFO) - ? LSS_NAME(restore_rt)() : LSS_NAME(restore)(); - } - #endif - } - rc = LSS_NAME(rt_sigaction)(signum, act ? &a : act, oldact, - (KERNEL_NSIG+7)/8); - if (rc < 0 && LSS_ERRNO == ENOSYS) { - struct kernel_old_sigaction oa, ooa, *ptr_a = &oa, *ptr_oa = &ooa; - if (!act) { - ptr_a = NULL; - } else { - oa.sa_handler_ = act->sa_handler_; - memcpy(&oa.sa_mask, &act->sa_mask, sizeof(oa.sa_mask)); - #ifndef __mips__ - oa.sa_restorer = act->sa_restorer; - #endif - oa.sa_flags = act->sa_flags; - } - if (!oldact) { - ptr_oa = NULL; - } - LSS_ERRNO = old_errno; - rc = LSS_NAME(_sigaction)(signum, ptr_a, ptr_oa); - if (rc == 0 && oldact) { - if (act) { - memcpy(oldact, act, sizeof(*act)); - } else { - memset(oldact, 0, sizeof(*oldact)); - } - oldact->sa_handler_ = ptr_oa->sa_handler_; - oldact->sa_flags = ptr_oa->sa_flags; - memcpy(&oldact->sa_mask, &ptr_oa->sa_mask, sizeof(ptr_oa->sa_mask)); - #ifndef __mips__ - oldact->sa_restorer = ptr_oa->sa_restorer; - #endif - } - } - return rc; - } - - LSS_INLINE int LSS_NAME(sigpending)(struct kernel_sigset_t *set) { - int old_errno = LSS_ERRNO; - int rc = LSS_NAME(rt_sigpending)(set, (KERNEL_NSIG+7)/8); - if (rc < 0 && LSS_ERRNO == ENOSYS) { - LSS_ERRNO = old_errno; - LSS_NAME(sigemptyset)(set); - rc = LSS_NAME(_sigpending)(&set->sig[0]); - } - return rc; - } - - LSS_INLINE int LSS_NAME(sigprocmask)(int how, - const struct kernel_sigset_t *set, - struct kernel_sigset_t *oldset) { - int olderrno = LSS_ERRNO; - int rc = LSS_NAME(rt_sigprocmask)(how, set, oldset, (KERNEL_NSIG+7)/8); - if (rc < 0 && LSS_ERRNO == ENOSYS) { - LSS_ERRNO = olderrno; - if (oldset) { - LSS_NAME(sigemptyset)(oldset); - } - rc = LSS_NAME(_sigprocmask)(how, - set ? &set->sig[0] : NULL, - oldset ? &oldset->sig[0] : NULL); - } - return rc; - } - - LSS_INLINE int LSS_NAME(sigsuspend)(const struct kernel_sigset_t *set) { - int olderrno = LSS_ERRNO; - int rc = LSS_NAME(rt_sigsuspend)(set, (KERNEL_NSIG+7)/8); - if (rc < 0 && LSS_ERRNO == ENOSYS) { - LSS_ERRNO = olderrno; - rc = LSS_NAME(_sigsuspend)( - #ifndef __PPC__ - set, 0, - #endif - set->sig[0]); - } - return rc; - } - #endif - #if defined(__PPC__) - #undef LSS_SC_LOADARGS_0 - #define LSS_SC_LOADARGS_0(dummy...) - #undef LSS_SC_LOADARGS_1 - #define LSS_SC_LOADARGS_1(arg1) \ - __sc_4 = (unsigned long) (arg1) - #undef LSS_SC_LOADARGS_2 - #define LSS_SC_LOADARGS_2(arg1, arg2) \ - LSS_SC_LOADARGS_1(arg1); \ - __sc_5 = (unsigned long) (arg2) - #undef LSS_SC_LOADARGS_3 - #define LSS_SC_LOADARGS_3(arg1, arg2, arg3) \ - LSS_SC_LOADARGS_2(arg1, arg2); \ - __sc_6 = (unsigned long) (arg3) - #undef LSS_SC_LOADARGS_4 - #define LSS_SC_LOADARGS_4(arg1, arg2, arg3, arg4) \ - LSS_SC_LOADARGS_3(arg1, arg2, arg3); \ - __sc_7 = (unsigned long) (arg4) - #undef LSS_SC_LOADARGS_5 - #define LSS_SC_LOADARGS_5(arg1, arg2, arg3, arg4, arg5) \ - LSS_SC_LOADARGS_4(arg1, arg2, arg3, arg4); \ - __sc_8 = (unsigned long) (arg5) - #undef LSS_SC_BODY - #define LSS_SC_BODY(nr, type, opt, args...) \ - long __sc_ret, __sc_err; \ - { \ - register unsigned long __sc_0 __asm__ ("r0") = __NR_socketcall; \ - register unsigned long __sc_3 __asm__ ("r3") = opt; \ - register unsigned long __sc_4 __asm__ ("r4"); \ - register unsigned long __sc_5 __asm__ ("r5"); \ - register unsigned long __sc_6 __asm__ ("r6"); \ - register unsigned long __sc_7 __asm__ ("r7"); \ - register unsigned long __sc_8 __asm__ ("r8"); \ - LSS_SC_LOADARGS_##nr(args); \ - __asm__ __volatile__ \ - ("stwu 1, -48(1)\n\t" \ - "stw 4, 20(1)\n\t" \ - "stw 5, 24(1)\n\t" \ - "stw 6, 28(1)\n\t" \ - "stw 7, 32(1)\n\t" \ - "stw 8, 36(1)\n\t" \ - "addi 4, 1, 20\n\t" \ - "sc\n\t" \ - "mfcr %0" \ - : "=&r" (__sc_0), \ - "=&r" (__sc_3), "=&r" (__sc_4), \ - "=&r" (__sc_5), "=&r" (__sc_6), \ - "=&r" (__sc_7), "=&r" (__sc_8) \ - : LSS_ASMINPUT_##nr \ - : "cr0", "ctr", "memory"); \ - __sc_ret = __sc_3; \ - __sc_err = __sc_0; \ - } \ - LSS_RETURN(type, __sc_ret, __sc_err) - - LSS_INLINE ssize_t LSS_NAME(recvmsg)(int s,struct kernel_msghdr *msg, - int flags){ - LSS_SC_BODY(3, ssize_t, 17, s, msg, flags); - } - - LSS_INLINE ssize_t LSS_NAME(sendmsg)(int s, - const struct kernel_msghdr *msg, - int flags) { - LSS_SC_BODY(3, ssize_t, 16, s, msg, flags); - } - - // TODO(csilvers): why is this ifdef'ed out? -#if 0 - LSS_INLINE ssize_t LSS_NAME(sendto)(int s, const void *buf, size_t len, - int flags, - const struct kernel_sockaddr *to, - unsigned int tolen) { - LSS_BODY(6, ssize_t, 11, s, buf, len, flags, to, tolen); - } -#endif - - LSS_INLINE int LSS_NAME(shutdown)(int s, int how) { - LSS_SC_BODY(2, int, 13, s, how); - } - - LSS_INLINE int LSS_NAME(socket)(int domain, int type, int protocol) { - LSS_SC_BODY(3, int, 1, domain, type, protocol); - } - - LSS_INLINE int LSS_NAME(socketpair)(int d, int type, int protocol, - int sv[2]) { - LSS_SC_BODY(4, int, 8, d, type, protocol, sv); - } - #endif - #if defined(__i386__) || defined(__ARM_ARCH_3__) || \ - (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) - #define __NR__socketcall __NR_socketcall - LSS_INLINE _syscall2(int, _socketcall, int, c, - va_list, a) - - LSS_INLINE int LSS_NAME(socketcall)(int op, ...) { - int rc; - va_list ap; - va_start(ap, op); - rc = LSS_NAME(_socketcall)(op, ap); - va_end(ap); - return rc; - } - - LSS_INLINE ssize_t LSS_NAME(recvmsg)(int s,struct kernel_msghdr *msg, - int flags){ - return (ssize_t)LSS_NAME(socketcall)(17, s, msg, flags); - } - - LSS_INLINE ssize_t LSS_NAME(sendmsg)(int s, - const struct kernel_msghdr *msg, - int flags) { - return (ssize_t)LSS_NAME(socketcall)(16, s, msg, flags); - } - - LSS_INLINE ssize_t LSS_NAME(sendto)(int s, const void *buf, size_t len, - int flags, - const struct kernel_sockaddr *to, - unsigned int tolen) { - return (ssize_t)LSS_NAME(socketcall)(11, s, buf, len, flags, to, tolen); - } - - LSS_INLINE int LSS_NAME(shutdown)(int s, int how) { - return LSS_NAME(socketcall)(13, s, how); - } - - LSS_INLINE int LSS_NAME(socket)(int domain, int type, int protocol) { - return LSS_NAME(socketcall)(1, domain, type, protocol); - } - - LSS_INLINE int LSS_NAME(socketpair)(int d, int type, int protocol, - int sv[2]) { - return LSS_NAME(socketcall)(8, d, type, protocol, sv); - } - #endif - #if defined(__i386__) || defined(__PPC__) - LSS_INLINE _syscall4(int, fstatat64, int, d, - const char *, p, - struct kernel_stat64 *, b, int, f) - #endif - #if defined(__i386__) || defined(__PPC__) || \ - (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) - LSS_INLINE _syscall3(pid_t, waitpid, pid_t, p, - int*, s, int, o) - #endif - #if defined(__mips__) - /* sys_pipe() on MIPS has non-standard calling conventions, as it returns - * both file handles through CPU registers. - */ - LSS_INLINE int LSS_NAME(pipe)(int *p) { - register unsigned long __v0 __asm__("$2") = __NR_pipe; - register unsigned long __v1 __asm__("$3"); - register unsigned long __r7 __asm__("$7"); - __asm__ __volatile__ ("syscall\n" - : "=&r"(__v0), "=&r"(__v1), "+r" (__r7) - : "0"(__v0) - : "$8", "$9", "$10", "$11", "$12", - "$13", "$14", "$15", "$24", "memory"); - if (__r7) { - LSS_ERRNO = __v0; - return -1; - } else { - p[0] = __v0; - p[1] = __v1; - return 0; - } - } - #else - LSS_INLINE _syscall1(int, pipe, int *, p) - #endif - /* TODO(csilvers): see if ppc can/should support this as well */ - #if defined(__i386__) || defined(__ARM_ARCH_3__) || \ - (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64) - #define __NR__statfs64 __NR_statfs64 - #define __NR__fstatfs64 __NR_fstatfs64 - LSS_INLINE _syscall3(int, _statfs64, const char*, p, - size_t, s,struct kernel_statfs64*, b) - LSS_INLINE _syscall3(int, _fstatfs64, int, f, - size_t, s,struct kernel_statfs64*, b) - LSS_INLINE int LSS_NAME(statfs64)(const char *p, - struct kernel_statfs64 *b) { - return LSS_NAME(_statfs64)(p, sizeof(*b), b); - } - LSS_INLINE int LSS_NAME(fstatfs64)(int f,struct kernel_statfs64 *b) { - return LSS_NAME(_fstatfs64)(f, sizeof(*b), b); - } - #endif - - LSS_INLINE int LSS_NAME(execv)(const char *path, const char *const argv[]) { - extern char **environ; - return LSS_NAME(execve)(path, argv, (const char *const *)environ); - } - - LSS_INLINE pid_t LSS_NAME(gettid)() { - pid_t tid = LSS_NAME(_gettid)(); - if (tid != -1) { - return tid; - } - return LSS_NAME(getpid)(); - } - - LSS_INLINE void *LSS_NAME(mremap)(void *old_address, size_t old_size, - size_t new_size, int flags, ...) { - va_list ap; - void *new_address, *rc; - va_start(ap, flags); - new_address = va_arg(ap, void *); - rc = LSS_NAME(_mremap)(old_address, old_size, new_size, - flags, new_address); - va_end(ap); - return rc; - } - - LSS_INLINE int LSS_NAME(ptrace_detach)(pid_t pid) { - /* PTRACE_DETACH can sometimes forget to wake up the tracee and it - * then sends job control signals to the real parent, rather than to - * the tracer. We reduce the risk of this happening by starting a - * whole new time slice, and then quickly sending a SIGCONT signal - * right after detaching from the tracee. - */ - int rc, err; - LSS_NAME(sched_yield)(); - rc = LSS_NAME(ptrace)(PTRACE_DETACH, pid, (void *)0, (void *)0); - err = LSS_ERRNO; - LSS_NAME(kill)(pid, SIGCONT); - LSS_ERRNO = err; - return rc; - } - - LSS_INLINE int LSS_NAME(raise)(int sig) { - return LSS_NAME(kill)(LSS_NAME(getpid)(), sig); - } - - LSS_INLINE int LSS_NAME(setpgrp)() { - return LSS_NAME(setpgid)(0, 0); - } - - LSS_INLINE int LSS_NAME(sysconf)(int name) { - extern int __getpagesize(void); - switch (name) { - case _SC_OPEN_MAX: { - struct kernel_rlimit limit; - return LSS_NAME(getrlimit)(RLIMIT_NOFILE, &limit) < 0 - ? 8192 : limit.rlim_cur; - } - case _SC_PAGESIZE: - return __getpagesize(); - default: - errno = ENOSYS; - return -1; - } - } - #if defined(__x86_64__) || \ - (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI64) - /* pread64() and pwrite64() do not exist on 64-bit systems... */ - LSS_INLINE _syscall3(int, readahead, int, f, - loff_t, o, unsigned, c) - #else - #define __NR__pread64 __NR_pread64 - #define __NR__pwrite64 __NR_pwrite64 - #define __NR__readahead __NR_readahead - LSS_INLINE _syscall5(ssize_t, _pread64, int, f, - void *, b, size_t, c, unsigned, o1, - unsigned, o2) - LSS_INLINE _syscall5(ssize_t, _pwrite64, int, f, - const void *, b, size_t, c, unsigned, o1, - long, o2) - LSS_INLINE _syscall4(int, _readahead, int, f, - unsigned, o1, unsigned, o2, size_t, c) - /* We force 64bit-wide parameters onto the stack, then access each - * 32-bit component individually. This guarantees that we build the - * correct parameters independent of the native byte-order of the - * underlying architecture. - */ - LSS_INLINE ssize_t LSS_NAME(pread64)(int fd, void *buf, size_t count, - loff_t off) { - union { loff_t off; unsigned arg[2]; } o = { off }; - return LSS_NAME(_pread64)(fd, buf, count, o.arg[0], o.arg[1]); - } - LSS_INLINE ssize_t LSS_NAME(pwrite64)(int fd, const void *buf, - size_t count, loff_t off) { - union { loff_t off; unsigned arg[2]; } o = { off }; - return LSS_NAME(_pwrite64)(fd, buf, count, o.arg[0], o.arg[1]); - } - LSS_INLINE int LSS_NAME(readahead)(int fd, loff_t off, int len) { - union { loff_t off; unsigned arg[2]; } o = { off }; - return LSS_NAME(_readahead)(fd, o.arg[0], o.arg[1], len); - } - #endif -#endif - -#if defined(__cplusplus) && !defined(SYS_CPLUSPLUS) -} -#endif - -#endif -#endif diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/memory.h b/toolkit/crashreporter/google-breakpad/src/common/linux/memory.h deleted file mode 100644 index f10a194b47a..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/memory.h +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright (c) 2009, 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_HANDLER_MEMORY_H_ -#define CLIENT_LINUX_HANDLER_MEMORY_H_ - -#include -#include -#include -#include - -#include "common/linux/linux_syscall_support.h" - -namespace google_breakpad { - -// This is very simple allocator which fetches pages from the kernel directly. -// Thus, it can be used even when the heap may be corrupted. -// -// There is no free operation. The pages are only freed when the object is -// destroyed. -class PageAllocator { - public: - PageAllocator() - : page_size_(getpagesize()), - last_(NULL), - current_page_(NULL), - page_offset_(0) { - } - - ~PageAllocator() { - FreeAll(); - } - - void *Alloc(unsigned bytes) { - if (!bytes) - return NULL; - - if (current_page_ && page_size_ - page_offset_ >= bytes) { - uint8_t *const ret = current_page_ + page_offset_; - page_offset_ += bytes; - if (page_offset_ == page_size_) { - page_offset_ = 0; - current_page_ = NULL; - } - - return ret; - } - - const unsigned pages = - (bytes + sizeof(PageHeader) + page_size_ - 1) / page_size_; - uint8_t *const ret = GetNPages(pages); - if (!ret) - return NULL; - - page_offset_ = (page_size_ - (page_size_ * pages - (bytes + sizeof(PageHeader)))) % page_size_; - current_page_ = page_offset_ ? ret + page_size_ * (pages - 1) : NULL; - - return ret + sizeof(PageHeader); - } - - private: - uint8_t *GetNPages(unsigned num_pages) { -#ifdef __x86_64 - void *a = sys_mmap(NULL, page_size_ * num_pages, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); -#else - void *a = sys_mmap2(NULL, page_size_ * num_pages, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); -#endif - if (a == MAP_FAILED) - return NULL; - - struct PageHeader *header = reinterpret_cast(a); - header->next = last_; - header->num_pages = num_pages; - last_ = header; - - return reinterpret_cast(a); - } - - void FreeAll() { - PageHeader *next; - - for (PageHeader *cur = last_; cur; cur = next) { - next = cur->next; - sys_munmap(cur, cur->num_pages * page_size_); - } - } - - struct PageHeader { - PageHeader *next; // pointer to the start of the next set of pages. - unsigned num_pages; // the number of pages in this set. - }; - - const unsigned page_size_; - PageHeader *last_; - uint8_t *current_page_; - unsigned page_offset_; -}; - -// A wasteful vector is like a normal std::vector, except that it's very much -// simplier and it allocates memory from a PageAllocator. It's wasteful -// because, when resizing, it always allocates a whole new array since the -// PageAllocator doesn't support realloc. -template -class wasteful_vector { - public: - wasteful_vector(PageAllocator *allocator, unsigned size_hint = 16) - : allocator_(allocator), - a_((T*) allocator->Alloc(sizeof(T) * size_hint)), - allocated_(size_hint), - used_(0) { - } - - void push_back(const T& new_element) { - if (used_ == allocated_) - Realloc(allocated_ * 2); - a_[used_++] = new_element; - } - - size_t size() const { - return used_; - } - - T& operator[](size_t index) { - return a_[index]; - } - - const T& operator[](size_t index) const { - return a_[index]; - } - - private: - void Realloc(unsigned new_size) { - T *new_array = - reinterpret_cast(allocator_->Alloc(sizeof(T) * new_size)); - memcpy(new_array, a_, used_ * sizeof(T)); - a_ = new_array; - allocated_ = new_size; - } - - PageAllocator *const allocator_; - T *a_; // pointer to an array of |allocated_| elements. - unsigned allocated_; // size of |a_|, in elements. - unsigned used_; // number of used slots in |a_|. -}; - -} // namespace google_breakpad - -inline void* operator new(size_t nbytes, - google_breakpad::PageAllocator& allocator) { - return allocator.Alloc(nbytes); -} - -#endif // CLIENT_LINUX_HANDLER_MEMORY_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/memory_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/memory_unittest.cc deleted file mode 100644 index 66c83465a1e..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/memory_unittest.cc +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2009, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "common/linux/memory.h" -#include "testing/gtest/include/gtest/gtest.h" - -using namespace google_breakpad; - -namespace { -typedef testing::Test PageAllocatorTest; -} - -TEST(PageAllocatorTest, Setup) { - PageAllocator allocator; -} - -TEST(PageAllocatorTest, SmallObjects) { - PageAllocator allocator; - - for (unsigned i = 1; i < 1024; ++i) { - uint8_t *p = reinterpret_cast(allocator.Alloc(i)); - ASSERT_FALSE(p == NULL); - memset(p, 0, i); - } -} - -TEST(PageAllocatorTest, LargeObject) { - PageAllocator allocator; - - uint8_t *p = reinterpret_cast(allocator.Alloc(10000)); - ASSERT_FALSE(p == NULL); - for (unsigned i = 1; i < 10; ++i) { - uint8_t *p = reinterpret_cast(allocator.Alloc(i)); - ASSERT_FALSE(p == NULL); - memset(p, 0, i); - } -} - -namespace { -typedef testing::Test WastefulVectorTest; -} - -TEST(WastefulVectorTest, Setup) { - PageAllocator allocator_; - wasteful_vector v(&allocator_); - ASSERT_EQ(v.size(), 0u); -} - -TEST(WastefulVectorTest, Simple) { - PageAllocator allocator_; - wasteful_vector v(&allocator_); - - for (unsigned i = 0; i < 256; ++i) - v.push_back(i); - ASSERT_EQ(v.size(), 256u); - for (unsigned i = 0; i < 256; ++i) - ASSERT_EQ(v[i], i); -} diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/module.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/module.cc deleted file mode 100644 index 69bec9cd54b..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/module.cc +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright (c) 2009, 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 -#include -#include "common/linux/module.h" - -namespace google_breakpad { - -Module::Module(const string &name, const string &os, - const string &architecture, const string &id) : - name_(name), - os_(os), - architecture_(architecture), - id_(id), - load_address_(0) { } - -Module::~Module() { - for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); it++) - delete it->second; - for (vector::iterator it = functions_.begin(); - it != functions_.end(); it++) - delete *it; -} - -void Module::SetLoadAddress(Address address) { - load_address_ = address; -} - -void Module::AddFunction(Function *function) { - functions_.push_back(function); -} - -void Module::AddFunctions(vector::iterator begin, - vector::iterator end) { - functions_.insert(functions_.end(), begin, end); -} - -Module::File *Module::FindFile(const string &name) { - // A tricky bit here. The key of each map entry needs to be a - // pointer to the entry's File's name string. This means that we - // can't do the initial lookup with any operation that would create - // an empty entry for us if the name isn't found (like, say, - // operator[] or insert do), because such a created entry's key will - // be a pointer the string passed as our argument. Since the key of - // a map's value type is const, we can't fix it up once we've - // created our file. lower_bound does the lookup without doing an - // insertion, and returns a good hint iterator to pass to insert. - // Our "destiny" is where we belong, whether we're there or not now. - FileByNameMap::iterator destiny = files_.lower_bound(&name); - if (destiny == files_.end() - || *destiny->first != name) { // Repeated string comparison, boo hoo. - File *file = new File; - file->name_ = name; - file->source_id_ = -1; - destiny = files_.insert(destiny, - FileByNameMap::value_type(&file->name_, file)); - } - return destiny->second; -} - -Module::File *Module::FindFile(const char *name) { - string name_string = name; - return FindFile(name_string); -} - -void Module::AssignSourceIds() { - // First, give every source file an id of -1. - for (FileByNameMap::iterator file_it = files_.begin(); - file_it != files_.end(); file_it++) - file_it->second->source_id_ = -1; - - // Next, mark all files actually cited by our functions' line number - // info, by setting each one's source id to zero. - for (vector::const_iterator func_it = functions_.begin(); - func_it != functions_.end(); func_it++) { - Function *func = *func_it; - for (vector::iterator line_it = func->lines_.begin(); - line_it != func->lines_.end(); line_it++) - line_it->file_->source_id_ = 0; - } - - // Finally, assign source ids to those files that have been marked. - // We could have just assigned source id numbers while traversing - // the line numbers, but doing it this way numbers the files in - // lexicographical order by name, which is neat. - int next_source_id = 0; - for (FileByNameMap::iterator file_it = files_.begin(); - file_it != files_.end(); file_it++) - if (! file_it->second->source_id_) - file_it->second->source_id_ = next_source_id++; -} - -bool Module::ReportError() { - fprintf(stderr, "error writing symbol file: %s\n", - strerror (errno)); - return false; -} - -bool Module::Write(FILE *stream) { - if (0 > fprintf(stream, "MODULE %s %s %s %s\n", - os_.c_str(), architecture_.c_str(), id_.c_str(), - name_.c_str())) - return ReportError(); - - // Write out files. - AssignSourceIds(); - for (FileByNameMap::iterator file_it = files_.begin(); - file_it != files_.end(); file_it++) { - File *file = file_it->second; - if (file->source_id_ >= 0) { - if (0 > fprintf(stream, "FILE %d %s\n", - file->source_id_, file->name_.c_str())) - return ReportError(); - } - } - - // Write out functions and their lines. - for (vector::const_iterator func_it = functions_.begin(); - func_it != functions_.end(); func_it++) { - Function *func = *func_it; - if (0 > fprintf(stream, "FUNC %lx %lx %lu %s\n", - (unsigned long) (func->address_ - load_address_), - (unsigned long) func->size_, - (unsigned long) func->parameter_size_, - func->name_.c_str())) - return ReportError(); - for (vector::iterator line_it = func->lines_.begin(); - line_it != func->lines_.end(); line_it++) - if (0 > fprintf(stream, "%lx %lx %d %d\n", - (unsigned long) (line_it->address_ - load_address_), - (unsigned long) line_it->size_, - line_it->number_, - line_it->file_->source_id_)) - return ReportError(); - } - - return true; -} - -} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/module.h b/toolkit/crashreporter/google-breakpad/src/common/linux/module.h deleted file mode 100644 index b91c0f9062a..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/module.h +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright (c) 2009, Google Inc. -*- mode: c++ -*- -// 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. - -// module.h: defines google_breakpad::Module, for writing breakpad symbol files - -#ifndef COMMON_LINUX_MODULE_H__ -#define COMMON_LINUX_MODULE_H__ - -#include -#include -#include -#include - -#include "google_breakpad/common/breakpad_types.h" - -namespace google_breakpad { - -using std::string; -using std::vector; -using std::map; - -// A Module represents the contents of a module, and supports methods -// for adding information produced by parsing STABS or DWARF data -// --- possibly both from the same file --- and then writing out the -// unified contents as a Breakpad-format symbol file. -class Module { - public: - // The type of addresses and sizes in a symbol table. - typedef u_int64_t Address; - struct File; - struct Function; - struct Line; - - // Addresses appearing in File, Function, and Line structures are - // absolute, not relative to the the module's load address. That - // is, if the module were loaded at its nominal load address, the - // addresses would be correct. - - // A source file. - struct File { - // The name of the source file. - string name_; - - // The file's source id. The Write member function clears this - // field and assigns source ids a fresh, so any value placed here - // before calling Write will be lost. - int source_id_; - }; - - // A function. - struct Function { - // For sorting by address. (Not style-guide compliant, but it's - // stupid not to put this in the struct.) - static bool CompareByAddress(const Function *x, const Function *y) { - return x->address_ < y->address_; - } - - // The function's name. - string name_; - - // The start address and length of the function's code. - Address address_, size_; - - // The function's parameter size. - Address parameter_size_; - - // Source lines belonging to this function, sorted by increasing - // address. - vector lines_; - }; - - // A source line. - struct Line { - // For sorting by address. (Not style-guide compliant, but it's - // stupid not to put this in the struct.) - static bool CompareByAddress(const Module::Line &x, const Module::Line &y) { - return x.address_ < y.address_; - } - - Address address_, size_; // The address and size of the line's code. - File *file_; // The source file. - int number_; // The source line number. - }; - - // Create a new module with the given name, operating system, - // architecture, and ID string. - Module(const string &name, const string &os, const string &architecture, - const string &id); - ~Module(); - - // Set the module's load address to LOAD_ADDRESS; addresses given - // for functions and lines will be written to the Breakpad symbol - // file as offsets from this address. Construction initializes this - // module's load address to zero: addresses written to the symbol - // file will be the same as they appear in the File and Line - // structures. - void SetLoadAddress(Address load_address); - - // Add FUNCTION to the module. - // Destroying this module frees all Function objects that have been - // added with this function. - void AddFunction(Function *function); - - // Add all the functions in [BEGIN,END) to the module. - // Destroying this module frees all Function objects that have been - // added with this function. - void AddFunctions(vector::iterator begin, - vector::iterator end); - - // If this module has a file named NAME, return a pointer to it. If - // it has none, then create one and return a pointer to the new - // file. Destroying this module frees all File objects that have - // been created using this function, or with Insert. - File *FindFile(const string &name); - File *FindFile(const char *name); - - // Write this module to STREAM in the breakpad symbol format. - // Return true if all goes well, or false if an error occurs. This - // method writes out: - // - a header based on the values given to the constructor, - // - the source files added via FindFile, and finally - // - the functions added via AddFunctions, each with its lines. - // Addresses in the output are all relative to the load address - // established by SetLoadAddress. - bool Write(FILE *stream); - -private: - - // Find those files in this module that are actually referred to by - // functions' line number data, and assign them source id numbers. - // Set the source id numbers for all other files --- unused by the - // source line data --- to -1. We do this before writing out the - // symbol file, at which point we omit any unused files. - void AssignSourceIds(); - - // Report an error that has occurred writing the symbol file, using - // errno to find the appropriate cause. Return false. - static bool ReportError(); - - // Module header entries. - string name_, os_, architecture_, id_; - - // The module's nominal load address. Addresses for functions and - // lines are absolute, assuming the module is loaded at this - // address. - Address load_address_; - - // Relation for maps whose keys are strings shared with some other - // structure. - struct CompareStringPtrs { - bool operator()(const string *x, const string *y) { return *x < *y; }; - }; - - // A map from filenames to File structures. The map's keys are - // pointers to the Files' names. - typedef map FileByNameMap; - - // The module owns all the files and functions that have been added - // to it; destroying the module frees the Files and Functions these - // point to. - FileByNameMap files_; // This module's source files. - vector functions_; // This module's functions. -}; - -} // namespace google_breakpad - -#endif // COMMON_LINUX_MODULE_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.cc deleted file mode 100644 index 570733613d7..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.cc +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2009 Google Inc. All Rights Reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// This file implements the google_breakpad::StabsReader class. - -#include -#include -#include -#include - -#include "common/linux/stabs_reader.h" - -namespace google_breakpad { - -StabsReader::StabsReader(const uint8_t *stab, size_t stab_size, - const uint8_t *stabstr, size_t stabstr_size, - StabsHandler *handler) : - stabstr_(stabstr), - stabstr_size_(stabstr_size), - handler_(handler), - symbol_(NULL), - current_source_file_(NULL) { - symbols_ = reinterpret_cast(stab); - symbols_end_ = symbols_ + (stab_size / sizeof (*symbols_)); -} - -const char *StabsReader::SymbolString() { - ptrdiff_t offset = symbol_->n_un.n_strx; - if (offset < 0 || (size_t) offset >= stabstr_size_) { - handler_->Warning("symbol %d: name offset outside the string section", - symbol_ - symbols_); - // Return our null string, to keep our promise about all names being - // taken from the string section. - offset = 0; - } - return reinterpret_cast(stabstr_ + offset); -} - -bool StabsReader::Process() { - symbol_ = symbols_; - while (symbol_ < symbols_end_) { - if (symbol_->n_type == N_SO) { - if (! ProcessCompilationUnit()) - return false; - } else - symbol_++; - } - return true; -} - -bool StabsReader::ProcessCompilationUnit() { - assert(symbol_ < symbols_end_ && symbol_->n_type == N_SO); - - // There may be an N_SO entry whose name ends with a slash, - // indicating the directory in which the compilation occurred. - // The build directory defaults to NULL. - const char *build_directory = NULL; - { - const char *name = SymbolString(); - if (name[0] && name[strlen(name) - 1] == '/') { - build_directory = name; - symbol_++; - } - } - - // We expect to see an N_SO entry with a filename next, indicating - // the start of the compilation unit. - { - if (symbol_ >= symbols_end_ || symbol_->n_type != N_SO) - return true; - const char *name = SymbolString(); - if (name[0] == '\0') - return true; - current_source_file_ = name; - } - - if (! handler_->StartCompilationUnit(current_source_file_, - SymbolValue(), - build_directory)) - return false; - - symbol_++; - - // The STABS documentation says that some compilers may emit - // additional N_SO units with names immediately following the first, - // and that they should be ignored. However, the original Breakpad - // STABS reader doesn't ignore them, so we won't either. - - // Process the body of the compilation unit, up to the next N_SO. - while (symbol_ < symbols_end_ && symbol_->n_type != N_SO) { - if (symbol_->n_type == N_FUN) { - if (! ProcessFunction()) - return false; - } else - // Ignore anything else. - symbol_++; - } - - // An N_SO with an empty name indicates the end of the compilation - // unit. Default to zero. - uint64_t ending_address = 0; - if (symbol_ < symbols_end_) { - assert(symbol_->n_type == N_SO); - const char *name = SymbolString(); - if (name[0] == '\0') { - ending_address = SymbolValue(); - symbol_++; - } - } - - if (! handler_->EndCompilationUnit(ending_address)) - return false; - - return true; -} - -bool StabsReader::ProcessFunction() { - assert(symbol_ < symbols_end_ && symbol_->n_type == N_FUN); - - uint64_t function_address = SymbolValue(); - // The STABS string for an N_FUN entry is the name of the function, - // followed by a colon, followed by type information for the - // function. We want to pass the name alone to StartFunction. - const char *stab_string = SymbolString(); - const char *name_end = strchr(stab_string, ':'); - if (! name_end) - name_end = stab_string + strlen(stab_string); - std::string name(stab_string, name_end - stab_string); - if (! handler_->StartFunction(name, function_address)) - return false; - symbol_++; - - while (symbol_ < symbols_end_) { - if (symbol_->n_type == N_SO || symbol_->n_type == N_FUN) - break; - else if (symbol_->n_type == N_SLINE) { - // The value of an N_SLINE entry is the offset of the line from - // the function's start address. - uint64_t line_address = function_address + SymbolValue(); - // The n_desc of a N_SLINE entry is the line number. It's a - // signed 16-bit field; line numbers from 32768 to 65535 are - // stored as n-65536. - uint16_t line_number = symbol_->n_desc; - if (! handler_->Line(line_address, current_source_file_, line_number)) - return false; - symbol_++; - } else if (symbol_->n_type == N_SOL) { - current_source_file_ = SymbolString(); - symbol_++; - } else - // Ignore anything else. - symbol_++; - } - - // If there is a subsequent N_SO or N_FUN entry, its address is our - // end address. - uint64_t ending_address = 0; - if (symbol_ < symbols_end_) { - assert(symbol_->n_type == N_SO || symbol_->n_type == N_FUN); - ending_address = SymbolValue(); - // Note: we do not increment symbol_ here, since we haven't consumed it. - } - - if (! handler_->EndFunction(ending_address)) - return false; - - return true; -} - -} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.h b/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.h deleted file mode 100644 index 7ebc30b1fd9..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.h +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2009 Google Inc. All Rights Reserved. -*- mode: c++ -*- -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// This file contains definitions related to the STABS reader and -// its handler interfaces. -// A description of the STABS debugging format can be found at -// http://sourceware.org/gdb/current/onlinedocs/stabs_toc.html -// The comments here assume you understand the format. -// -// This reader assumes that the system's and -// headers accurately describe the layout of the STABS data; this code -// is not cross-platform safe. - -#ifndef COMMON_LINUX_STABS_READER_H__ -#define COMMON_LINUX_STABS_READER_H__ - -#include -#include -#include - -#include - -namespace google_breakpad { - -class StabsHandler; - -class StabsReader { - public: - // Create a reader for the STABS debug information whose .stab - // section is the STAB_SIZE bytes at STAB, and whose .stabstr - // section is the STABSTR_SIZE bytes at STABSTR. The reader will - // call the methods of HANDLER to report the information it finds, - // when the reader's 'process' method is called. - // - // Note that, in ELF, the .stabstr section should be found using the - // 'sh_link' field of the .stab section header, not by name. - StabsReader(const uint8_t *stab, size_t stab_size, - const uint8_t *stabstr, size_t stabstr_size, - StabsHandler *handler); - - // Process the STAB data, calling the handler's methods to report - // what we find. While the handler functions return true, continue - // to process until we reach the end of the section. If we - // processed the entire section and all handlers returned true, - // return true. If any handler returned false, return false. - bool Process(); - - private: - // Return the name of the current symbol. - const char *SymbolString(); - - // Return the value of the current symbol. - const uint64_t SymbolValue() { - return symbol_->n_value; - } - - // Process a compilation unit starting at symbol_. Return true - // to continue processing, or false to abort. - bool ProcessCompilationUnit(); - - // Process a function in current_source_file_ starting at symbol_. - // Return true to continue processing, or false to abort. - bool ProcessFunction(); - - // The debugging information we're reading. - const struct nlist *symbols_, *symbols_end_; - const uint8_t *stabstr_; - size_t stabstr_size_; - - StabsHandler *handler_; - - // The current symbol we're processing. - const struct nlist *symbol_; - - // The current source file name. - const char *current_source_file_; -}; - -// Consumer-provided callback structure for the STABS reader. -// Clients of the STABS reader provide an instance of this structure. -// The reader then invokes the methods of that instance to report the -// information it finds. -// -// The default definitions of the methods do nothing. -class StabsHandler { - public: - StabsHandler() { } - virtual ~StabsHandler() { } - - // Some general notes about the handler callback functions: - - // Processing proceeds until the end of the .stabs section, or until - // one of these functions returns false. - - // The addresses given are as reported in the STABS info, without - // regard for whether the module may be loaded at different - // addresses at different times (a shared library, say). When - // processing STABS from an ELF shared library, the addresses given - // all assume the library is loaded at its nominal load address. - // They are *not* offsets from the nominal load address. If you - // want offsets, you must subtract off the library's nominal load - // address. - - // The arguments to these functions named FILENAME are all - // references to strings stored in the .stabstr section. Because - // both the Linux and Solaris linkers factor out duplicate strings - // from the .stabstr section, the consumer can assume that if two - // FILENAME values are different addresses, they represent different - // file names. - // - // Thus, it's safe to use (say) std::map, which does - // address comparisons. Since all the pointers are into the array - // holding the .stabstr section's contents, comparing them produces - // predictable results. - - // Begin processing a compilation unit whose main source file is - // named FILENAME, and whose base address is ADDRESS. If - // BUILD_DIRECTORY is non-NULL, it is the name of the build - // directory in which the compilation occurred. - virtual bool StartCompilationUnit(const char *filename, uint64_t address, - const char *build_directory) { - return true; - } - - // Finish processing the compilation unit. If END_ADDRESS is - // non-zero, it is the ending address of the compilation unit. This - // information may not be available, in which case the consumer must - // infer it by other means. - virtual bool EndCompilationUnit(uint64_t address) { return true; } - - // Begin processing a function named NAME, whose starting address is - // ADDRESS. This function belongs to the compilation unit that was - // most recently started but not ended. - // - // Note that, unlike filenames, NAME is not a pointer into the - // .stabstr section; this is because the name as it appears in the - // STABS data is followed by type information. The value passed to - // StartFunction is the function name alone. - virtual bool StartFunction(const std::string &name, uint64_t address) { - return true; - } - - // Finishing processing the function. If END_ADDRESS is non-zero, - // it is the ending address for the function. This information may - // not be available, in which case the consumer must infer it by - // other means. - virtual bool EndFunction(uint64_t address) { return true; } - - // Report that the code at ADDRESS is attributable to line NUMBER of - // the source file named FILENAME. The caller must infer the ending - // address of the line. - virtual bool Line(uint64_t address, const char *filename, int number) { - return true; - } - - // Report a warning. FORMAT is a printf-like format string, - // specifying how to format the subsequent arguments. - virtual void Warning(const char *format, ...) { } -}; - -} // namespace google_breakpad - -#endif // COMMON_LINUX_STABS_READER_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/SimpleStringDictionary.mm b/toolkit/crashreporter/google-breakpad/src/common/mac/SimpleStringDictionary.mm index d9c791cc1a2..567855e0c33 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/mac/SimpleStringDictionary.mm +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/SimpleStringDictionary.mm @@ -31,8 +31,6 @@ // Simple string dictionary that does not allocate memory // -#include - #import "SimpleStringDictionary.h" namespace google_breakpad { diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.h b/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.h index 5ed3e3b6f6c..12d8f7d6e75 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.h +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.h @@ -37,7 +37,7 @@ // This will map from an architecture string to a SectionMap, which // will contain the offsets for all the sections in the dictionary -typedef map ArchSectionMap; +typedef hash_map ArchSectionMap; @interface DumpSymbols : NSObject { @protected diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.mm b/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.mm index 8f0b9fe1554..88f1f4f15da 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.mm +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.mm @@ -68,6 +68,15 @@ static NSString *kHeaderCPUTypeKey = @"cpuType"; // for pruning out extraneous non-function symbols. static const int kTextSection = 1; +namespace __gnu_cxx { +template<> + struct hash { + size_t operator()(const std::string& k) const { + return hash< const char* >()( k.c_str() ); + } +}; +} + // Dump FunctionMap to stdout. Print address, function name, file // name, line number, lowpc, and highpc if available. void DumpFunctionMap(const dwarf2reader::FunctionMap function_map) { @@ -312,16 +321,13 @@ void DumpFunctionMap(const dwarf2reader::FunctionMap function_map) { //============================================================================= - (BOOL)loadSymbolInfo:(void *)base offset:(uint32_t)offset { - BOOL loadedStabs = [self loadSTABSSymbolInfo:base offset:offset]; - NSMutableDictionary *archSections = [sectionData_ objectForKey:architecture_]; - BOOL loadedDWARF = NO; if ([archSections objectForKey:@"__DWARF__debug_info"]) { // Treat this this as debug information - loadedDWARF = [self loadDWARFSymbolInfo:base offset:offset]; + return [self loadDWARFSymbolInfo:base offset:offset]; } - return loadedDWARF || loadedStabs; + return [self loadSTABSSymbolInfo:base offset:offset]; } //============================================================================= @@ -336,15 +342,11 @@ void DumpFunctionMap(const dwarf2reader::FunctionMap function_map) { section *dbgInfoSection = [[archSections objectForKey:@"__DWARF__debug_info"] sectionPointer]; uint32_t debugInfoSize = SwapLongIfNeeded(dbgInfoSection->size); -#if __BIG_ENDIAN__ - dwarf2reader::ByteReader byte_reader(swap ? - dwarf2reader::ENDIANNESS_LITTLE : - dwarf2reader::ENDIANNESS_BIG); -#elif __LITTLE_ENDIAN__ + // i think this will break if run on a big-endian machine dwarf2reader::ByteReader byte_reader(swap ? dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE); -#endif + uint64_t dbgOffset = 0; dwarf2reader::SectionMap* oneArchitectureSectionMap = [self getSectionMapForArchitecture:architecture_]; diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader-inl.h b/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader-inl.h index daf9120647b..7ccd0fe68e2 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader-inl.h +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader-inl.h @@ -37,11 +37,9 @@ inline uint8 ByteReader::ReadOneByte(const char* buffer) const { return buffer[0]; } -inline uint16 ByteReader::ReadTwoBytes(const char* signed_buffer) const { - const unsigned char *buffer - = reinterpret_cast(signed_buffer); - const uint16 buffer0 = buffer[0]; - const uint16 buffer1 = buffer[1]; +inline uint16 ByteReader::ReadTwoBytes(const char* buffer) const { + const uint16 buffer0 = static_cast(buffer[0]); + const uint16 buffer1 = static_cast(buffer[1]); if (endian_ == ENDIANNESS_LITTLE) { return buffer0 | buffer1 << 8; } else { @@ -49,13 +47,11 @@ inline uint16 ByteReader::ReadTwoBytes(const char* signed_buffer) const { } } -inline uint64 ByteReader::ReadFourBytes(const char* signed_buffer) const { - const unsigned char *buffer - = reinterpret_cast(signed_buffer); - const uint32 buffer0 = buffer[0]; - const uint32 buffer1 = buffer[1]; - const uint32 buffer2 = buffer[2]; - const uint32 buffer3 = buffer[3]; +inline uint64 ByteReader::ReadFourBytes(const char* buffer) const { + const uint32 buffer0 = static_cast(buffer[0]); + const uint32 buffer1 = static_cast(buffer[1]); + const uint32 buffer2 = static_cast(buffer[2]); + const uint32 buffer3 = static_cast(buffer[3]); if (endian_ == ENDIANNESS_LITTLE) { return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24; } else { @@ -63,17 +59,15 @@ inline uint64 ByteReader::ReadFourBytes(const char* signed_buffer) const { } } -inline uint64 ByteReader::ReadEightBytes(const char* signed_buffer) const { - const unsigned char *buffer - = reinterpret_cast(signed_buffer); - const uint64 buffer0 = buffer[0]; - const uint64 buffer1 = buffer[1]; - const uint64 buffer2 = buffer[2]; - const uint64 buffer3 = buffer[3]; - const uint64 buffer4 = buffer[4]; - const uint64 buffer5 = buffer[5]; - const uint64 buffer6 = buffer[6]; - const uint64 buffer7 = buffer[7]; +inline uint64 ByteReader::ReadEightBytes(const char* buffer) const { + const uint64 buffer0 = static_cast(buffer[0]); + const uint64 buffer1 = static_cast(buffer[1]); + const uint64 buffer2 = static_cast(buffer[2]); + const uint64 buffer3 = static_cast(buffer[3]); + const uint64 buffer4 = static_cast(buffer[4]); + const uint64 buffer5 = static_cast(buffer[5]); + const uint64 buffer6 = static_cast(buffer[6]); + const uint64 buffer7 = static_cast(buffer[7]); if (endian_ == ENDIANNESS_LITTLE) { return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24 | buffer4 << 32 | buffer5 << 40 | buffer6 << 48 | buffer7 << 56; diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader.cc b/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader.cc index 906fbb3229b..823cf2b6b86 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader.cc +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader.cc @@ -26,9 +26,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include - #include "common/mac/dwarf/bytereader-inl.h" + #include "common/mac/dwarf/bytereader.h" namespace dwarf2reader { diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.cc b/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.cc index e43029cc92c..6e7a2f1d1da 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.cc +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.cc @@ -26,8 +26,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include - +#include #include #include @@ -36,6 +35,17 @@ #include "common/mac/dwarf/bytereader.h" #include "common/mac/dwarf/line_state_machine.h" +namespace __gnu_cxx +{ + template<> struct hash< std::string > + { + size_t operator()( const std::string& x ) const + { + return hash< const char* >()( x.c_str() ); + } + }; +} + namespace dwarf2reader { // Read a DWARF2/3 initial length field from START, using READER, and diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.h b/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.h index cb47d97f656..f27cdac7dc9 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.h +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.h @@ -36,8 +36,8 @@ #ifndef COMMON_MAC_DWARF_DWARF2READER_H__ #define COMMON_MAC_DWARF_DWARF2READER_H__ +#include #include -#include #include #include #include @@ -46,6 +46,7 @@ #include "common/mac/dwarf/types.h" using namespace std; +using namespace __gnu_cxx; namespace dwarf2reader { struct LineStateMachine; @@ -55,7 +56,7 @@ class LineInfoHandler; // This maps from a string naming a section to a pair containing a // the data for the section, and the size of the section. -typedef map > SectionMap; +typedef hash_map > SectionMap; typedef list > AttributeList; typedef AttributeList::iterator AttributeIterator; typedef AttributeList::const_iterator ConstAttributeIterator; diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/functioninfo.cc b/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/functioninfo.cc index 04165dc0c1f..b6d3f0fe197 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/functioninfo.cc +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/functioninfo.cc @@ -29,17 +29,26 @@ // This is a client for the dwarf2reader to extract function and line // information from the debug info. -#include - #include #include #include + #include "common/mac/dwarf/functioninfo.h" #include "common/mac/dwarf/bytereader.h" +namespace __gnu_cxx +{ + template<> + struct hash + { + size_t operator()(const std::string& k) const; + }; +} + + namespace dwarf2reader { // Given an offset value, its form, and the base offset of the diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.cc b/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.cc index 9cd0d2e393b..0392627f66a 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.cc +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.cc @@ -118,13 +118,11 @@ bool PDBSourceLineWriter::PrintLines(IDiaEnumLineNumbers *lines) { return false; } - DWORD dia_source_id; - if (FAILED(line->get_sourceFileId(&dia_source_id))) { + DWORD source_id; + if (FAILED(line->get_sourceFileId(&source_id))) { fprintf(stderr, "failed to get line source file id\n"); return false; } - // duplicate file names are coalesced to share one ID - DWORD source_id = GetRealFileID(dia_source_id); DWORD line_num; if (FAILED(line->get_lineNumber(&line_num))) { @@ -138,18 +136,17 @@ bool PDBSourceLineWriter::PrintLines(IDiaEnumLineNumbers *lines) { return true; } -bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function, - IDiaSymbol *block) { +bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function) { // The function format is: // FUNC
DWORD rva; - if (FAILED(block->get_relativeVirtualAddress(&rva))) { + if (FAILED(function->get_relativeVirtualAddress(&rva))) { fprintf(stderr, "couldn't get rva\n"); return false; } ULONGLONG length; - if (FAILED(block->get_length(&length))) { + if (FAILED(function->get_length(&length))) { fprintf(stderr, "failed to get function length\n"); return false; } @@ -218,16 +215,7 @@ bool PDBSourceLineWriter::PrintSourceFiles() { return false; } - wstring file_name_string(file_name); - if (!FileIDIsCached(file_name_string)) { - // this is a new file name, cache it and output a FILE line. - CacheFileID(file_name_string, file_id); - fwprintf(output_, L"FILE %d %s\n", file_id, file_name); - } else { - // this file name has already been seen, just save this - // ID for later lookup. - StoreDuplicateFileID(file_name_string, file_id); - } + fwprintf(output_, L"FILE %d %s\n", file_id, file_name); file.Release(); } compiland.Release(); @@ -267,7 +255,7 @@ bool PDBSourceLineWriter::PrintFunctions() { // that PDBSourceLineWriter will output either a FUNC or PUBLIC line, // but not both. if (tag == SymTagFunction) { - if (!PrintFunction(symbol, symbol)) { + if (!PrintFunction(symbol)) { return false; } } else if (tag == SymTagPublicSymbol) { @@ -278,64 +266,6 @@ bool PDBSourceLineWriter::PrintFunctions() { symbol.Release(); } while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1); - // When building with PGO, the compiler can split functions into - // "hot" and "cold" blocks, and move the "cold" blocks out to separate - // pages, so the function can be noncontiguous. To find these blocks, - // we have to iterate over all the compilands, and then find blocks - // that are children of them. We can then find the lexical parents - // of those blocks and print out an extra FUNC line for blocks - // that are not contained in their parent functions. - CComPtr global; - if (FAILED(session_->get_globalScope(&global))) { - fprintf(stderr, "get_globalScope failed\n"); - return false; - } - - CComPtr compilands; - if (FAILED(global->findChildren(SymTagCompiland, NULL, - nsNone, &compilands))) { - fprintf(stderr, "findChildren failed on the global\n"); - return false; - } - - CComPtr compiland; - while (SUCCEEDED(compilands->Next(1, &compiland, &count)) && count == 1) { - CComPtr blocks; - if (FAILED(compiland->findChildren(SymTagBlock, NULL, - nsNone, &blocks))) { - fprintf(stderr, "findChildren failed on a compiland\n"); - return false; - } - - CComPtr block; - while (SUCCEEDED(blocks->Next(1, &block, &count)) && count == 1) { - // find this block's lexical parent function - CComPtr parent; - DWORD tag; - if (SUCCEEDED(block->get_lexicalParent(&parent)) && - SUCCEEDED(parent->get_symTag(&tag)) && - tag == SymTagFunction) { - // now get the block's offset and the function's offset and size, - // and determine if the block is outside of the function - DWORD func_rva, block_rva; - ULONGLONG func_length; - if (SUCCEEDED(block->get_relativeVirtualAddress(&block_rva)) && - SUCCEEDED(parent->get_relativeVirtualAddress(&func_rva)) && - SUCCEEDED(parent->get_length(&func_length))) { - if (block_rva < func_rva || block_rva > (func_rva + func_length)) { - if (!PrintFunction(parent, block)) { - return false; - } - } - } - } - parent.Release(); - block.Release(); - } - blocks.Release(); - compiland.Release(); - } - return true; } diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.h b/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.h index 8524d8b0532..e91d0737c93 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.h +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.h @@ -35,7 +35,6 @@ #include -#include #include struct IDiaEnumLineNumbers; @@ -45,7 +44,6 @@ struct IDiaSymbol; namespace google_breakpad { using std::wstring; -using stdext::hash_map; // A structure that carries information that identifies a pdb file. struct PDBModuleInfo { @@ -113,11 +111,8 @@ class PDBSourceLineWriter { bool PrintLines(IDiaEnumLineNumbers *lines); // Outputs a function address and name, followed by its source line list. - // block can be the same object as function, or it can be a reference - // to a code block that is lexically part of this function, but - // resides at a separate address. // Returns true on success. - bool PrintFunction(IDiaSymbol *function, IDiaSymbol *block); + bool PrintFunction(IDiaSymbol *function); // Outputs all functions as described above. Returns true on success. bool PrintFunctions(); @@ -139,37 +134,6 @@ class PDBSourceLineWriter { // its uuid and age. bool PrintPDBInfo(); - // Returns true if this filename has already been seen, - // and an ID is stored for it, or false if it has not. - bool FileIDIsCached(const wstring &file) { - return unique_files_.find(file) != unique_files_.end(); - }; - - // Cache this filename and ID for later reuse. - void CacheFileID(const wstring &file, DWORD id) { - unique_files_[file] = id; - }; - - // Store this ID in the cache as a duplicate for this filename. - void StoreDuplicateFileID(const wstring &file, DWORD id) { - hash_map::iterator iter = unique_files_.find(file); - if (iter != unique_files_.end()) { - // map this id to the previously seen one - file_ids_[id] = iter->second; - } - }; - - // Given a file's unique ID, return the ID that should be used to - // reference it. There may be multiple files with identical filenames - // but different unique IDs. The cache attempts to coalesce these into - // one ID per unique filename. - DWORD GetRealFileID(DWORD id) { - hash_map::iterator iter = file_ids_.find(id); - if (iter == file_ids_.end()) - return id; - return iter->second; - }; - // Returns the function name for a symbol. If possible, the name is // undecorated. If the symbol's decorated form indicates the size of // parameters on the stack, this information is returned in stack_param_size. @@ -189,13 +153,6 @@ class PDBSourceLineWriter { // The current output file for this WriteMap invocation. FILE *output_; - // There may be many duplicate filenames with different IDs. - // This maps from the DIA "unique ID" to a single ID per unique - // filename. - hash_map file_ids_; - // This maps unique filenames to file IDs. - hash_map unique_files_; - // Disallow copy ctor and operator= PDBSourceLineWriter(const PDBSourceLineWriter&); void operator=(const PDBSourceLineWriter&); diff --git a/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_exception_win32.h b/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_exception_win32.h index f052401c383..7fd4bc4fe75 100644 --- a/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_exception_win32.h +++ b/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_exception_win32.h @@ -94,11 +94,8 @@ typedef enum { /* EXCEPTION_PRIV_INSTRUCTION */ MD_EXCEPTION_CODE_WIN_STACK_OVERFLOW = 0xc00000fd, /* EXCEPTION_STACK_OVERFLOW */ - MD_EXCEPTION_CODE_WIN_POSSIBLE_DEADLOCK = 0xc0000194, + MD_EXCEPTION_CODE_WIN_POSSIBLE_DEADLOCK = 0xc0000194 /* EXCEPTION_POSSIBLE_DEADLOCK */ - MD_EXCEPTION_CODE_WIN_UNHANDLED_CPP_EXCEPTION = 0xe06d7363 - /* Per http://support.microsoft.com/kb/185294, - generated by Visual C++ compiler */ } MDExceptionCodeWin; diff --git a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/basic_source_line_resolver.h b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/basic_source_line_resolver.h index c01cc685e08..38759579f5f 100644 --- a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/basic_source_line_resolver.h +++ b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/basic_source_line_resolver.h @@ -33,14 +33,28 @@ #ifndef GOOGLE_BREAKPAD_PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_H__ #define GOOGLE_BREAKPAD_PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_H__ +// TODO: Platforms that have no hash_map can use map, at the likely cost of +// performance. +#ifdef __SUNPRO_CC +#define BSLR_NO_HASH_MAP +#endif // __SUNPRO_CC + +#ifdef BSLR_NO_HASH_MAP #include +#else // BSLR_NO_HASH_MAP +#include +#endif // BSLR_NO_HASH_MAP #include "google_breakpad/processor/source_line_resolver_interface.h" namespace google_breakpad { using std::string; +#ifdef BSLR_NO_HASH_MAP using std::map; +#else // BSLR_NO_HASH_MAP +using __gnu_cxx::hash_map; +#endif // BSLR_NO_HASH_MAP class BasicSourceLineResolver : public SourceLineResolverInterface { public: @@ -71,13 +85,23 @@ class BasicSourceLineResolver : public SourceLineResolverInterface { struct Function; struct PublicSymbol; struct File; +#ifdef BSLR_NO_HASH_MAP struct CompareString { bool operator()(const string &s1, const string &s2) const; }; +#else // BSLR_NO_HASH_MAP + struct HashString { + size_t operator()(const string &s) const; + }; +#endif // BSLR_NO_HASH_MAP class Module; // All of the modules we've loaded +#ifdef BSLR_NO_HASH_MAP typedef map ModuleMap; +#else // BSLR_NO_HASH_MAP + typedef hash_map ModuleMap; +#endif // BSLR_NO_HASH_MAP ModuleMap *modules_; // Disallow unwanted copy ctor and assignment operator diff --git a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump.h b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump.h index d3a7b92b1ca..f08a1c1833c 100644 --- a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump.h +++ b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump.h @@ -639,46 +639,6 @@ class MinidumpException : public MinidumpStream { MinidumpContext* context_; }; -// MinidumpAssertion wraps MDRawAssertionInfo, which contains information -// about an assertion that caused the minidump to be generated. -class MinidumpAssertion : public MinidumpStream { - public: - virtual ~MinidumpAssertion(); - - const MDRawAssertionInfo* assertion() const { - return valid_ ? &assertion_ : NULL; - } - - string expression() const { - return valid_ ? expression_ : ""; - } - - string function() const { - return valid_ ? function_ : ""; - } - - string file() const { - return valid_ ? file_ : ""; - } - - // Print a human-readable representation of the object to stdout. - void Print(); - - private: - friend class Minidump; - - static const u_int32_t kStreamType = MD_ASSERTION_INFO_STREAM; - - explicit MinidumpAssertion(Minidump* minidump); - - bool Read(u_int32_t expected_size); - - MDRawAssertionInfo assertion_; - string expression_; - string function_; - string file_; -}; - // MinidumpSystemInfo wraps MDRawSystemInfo and provides information about // the system on which the minidump was generated. See also MinidumpMiscInfo. @@ -828,7 +788,6 @@ class Minidump { MinidumpModuleList* GetModuleList(); MinidumpMemoryList* GetMemoryList(); MinidumpException* GetException(); - MinidumpAssertion* GetAssertion(); MinidumpSystemInfo* GetSystemInfo(); MinidumpMiscInfo* GetMiscInfo(); MinidumpBreakpadInfo* GetBreakpadInfo(); diff --git a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump_processor.h b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump_processor.h index 756a868beb2..f313bf71df2 100644 --- a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump_processor.h +++ b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump_processor.h @@ -141,11 +141,6 @@ class MinidumpProcessor { return (p != PROCESS_SYMBOL_SUPPLIER_INTERRUPTED); } - // Returns a textual representation of an assertion included - // in the minidump. Returns an empty string if this information - // does not exist or cannot be determined. - static string GetAssertion(Minidump *dump); - private: SymbolSupplier *supplier_; SourceLineResolverInterface *resolver_; diff --git a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/process_state.h b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/process_state.h index 230142278b9..afbbb1937db 100644 --- a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/process_state.h +++ b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/process_state.h @@ -36,9 +36,8 @@ #include #include -#include "google_breakpad/common/breakpad_types.h" #include "google_breakpad/processor/system_info.h" -#include "google_breakpad/processor/minidump.h" +#include "google_breakpad/common/breakpad_types.h" namespace google_breakpad { @@ -61,12 +60,8 @@ class ProcessState { bool crashed() const { return crashed_; } string crash_reason() const { return crash_reason_; } u_int64_t crash_address() const { return crash_address_; } - string assertion() const { return assertion_; } int requesting_thread() const { return requesting_thread_; } const vector* threads() const { return &threads_; } - const vector* thread_memory_regions() const { - return &thread_memory_regions_; - } const SystemInfo* system_info() const { return &system_info_; } const CodeModules* modules() const { return modules_; } @@ -93,11 +88,6 @@ class ProcessState { // this will be the address of the instruction that caused the fault. u_int64_t crash_address_; - // If there was an assertion that was hit, a textual representation - // of that assertion, possibly including the file and line at which - // it occurred. - string assertion_; - // The index of the thread that requested a dump be written in the // threads vector. If a dump was produced as a result of a crash, this // will point to the thread that crashed. If the dump was produced as @@ -111,7 +101,6 @@ class ProcessState { // Stacks for each thread (except possibly the exception handler // thread) at the time of the crash. vector threads_; - vector thread_memory_regions_; // OS and CPU information. SystemInfo system_info_; diff --git a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stack_frame_cpu.h b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stack_frame_cpu.h index 3d3003b7464..70823b9cd71 100644 --- a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stack_frame_cpu.h +++ b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stack_frame_cpu.h @@ -58,23 +58,7 @@ struct StackFrameX86 : public StackFrame { CONTEXT_VALID_ALL = -1 }; - // Indicates how well we trust the instruction pointer we derived - // during stack walking. Since the stack walker can resort to - // stack scanning, we can wind up with dubious frames. - // In rough order of "trust metric". - enum FrameTrust { - FRAME_TRUST_NONE, // Unknown - FRAME_TRUST_SCAN, // Scanned the stack, found this - FRAME_TRUST_CFI_SCAN, // Scanned the stack using call frame info, found this - FRAME_TRUST_FP, // Derived from frame pointer - FRAME_TRUST_CFI, // Derived from call frame info - FRAME_TRUST_CONTEXT // Given as instruction pointer in a context - }; - - StackFrameX86() - : context(), - context_validity(CONTEXT_VALID_NONE), - trust(FRAME_TRUST_NONE) {} + StackFrameX86() : context(), context_validity(CONTEXT_VALID_NONE) {} // Register state. This is only fully valid for the topmost frame in a // stack. In other frames, the values of nonvolatile registers may be @@ -86,10 +70,6 @@ struct StackFrameX86 : public StackFrame { // the OR operator doesn't work well with enumerated types. This indicates // which fields in context are valid. int context_validity; - - // Amount of trust the stack walker has in the instruction pointer - // of this frame. - FrameTrust trust; }; struct StackFramePPC : public StackFrame { diff --git a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stackwalker.h b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stackwalker.h index 90274aae10c..c463fd802df 100644 --- a/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stackwalker.h +++ b/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stackwalker.h @@ -42,7 +42,6 @@ #define GOOGLE_BREAKPAD_PROCESSOR_STACKWALKER_H__ #include -#include "google_breakpad/common/breakpad_types.h" namespace google_breakpad { @@ -96,16 +95,6 @@ class Stackwalker { SymbolSupplier *supplier, SourceLineResolverInterface *resolver); - // This can be used to filter out potential return addresses when - // the stack walker resorts to stack scanning. - // Returns true if any of: - // * This address is within a loaded module, but we don't have symbols - // for that module. - // * This address is within a loaded module for which we have symbols, - // and falls inside a function in that module. - // Returns false otherwise. - bool InstructionAddressSeemsValid(u_int64_t address); - // Information about the system that produced the minidump. Subclasses // and the SymbolSupplier may find this information useful. const SystemInfo *system_info_; diff --git a/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver.cc b/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver.cc index 63a94a3acc9..fe04439ecb0 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver.cc +++ b/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver.cc @@ -51,6 +51,9 @@ using std::map; using std::vector; using std::make_pair; +#ifndef BSLR_NO_HASH_MAP +using __gnu_cxx::hash; +#endif // BSLR_NO_HASH_MAP namespace google_breakpad { @@ -122,7 +125,11 @@ class BasicSourceLineResolver::Module { private: friend class BasicSourceLineResolver; +#ifdef BSLR_NO_HASH_MAP typedef map FileMap; +#else // BSLR_NO_HASH_MAP + typedef hash_map FileMap; +#endif // BSLR_NO_HASH_MAP // The types for stack_info_. This is equivalent to MS DIA's // StackFrameTypeEnum. Each identifies a different type of frame @@ -695,9 +702,15 @@ bool BasicSourceLineResolver::Module::ParseStackInfo(char *stack_info_line) { return true; } +#ifdef BSLR_NO_HASH_MAP bool BasicSourceLineResolver::CompareString::operator()( const string &s1, const string &s2) const { return strcmp(s1.c_str(), s2.c_str()) < 0; } +#else // BSLR_NO_HASH_MAP +size_t BasicSourceLineResolver::HashString::operator()(const string &s) const { + return hash()(s.c_str()); +} +#endif // BSLR_NO_HASH_MAP } // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/processor/minidump.cc b/toolkit/crashreporter/google-breakpad/src/processor/minidump.cc index ab4e428603c..8ce70cc878d 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/minidump.cc +++ b/toolkit/crashreporter/google-breakpad/src/processor/minidump.cc @@ -243,15 +243,6 @@ static string* UTF16ToUTF8(const vector& in, return out.release(); } -// Return the smaller of the number of code units in the UTF-16 string, -// not including the terminating null word, or maxlen. -static size_t UTF16codeunits(const u_int16_t *string, size_t maxlen) { - size_t count = 0; - while (count < maxlen && string[count] != 0) - count++; - return count; -} - // // MinidumpObject @@ -617,9 +608,8 @@ bool MinidumpContext::Read(u_int32_t expected_size) { } default: { - // Unknown context type - Don't log as an error yet. Let the - // caller work that out. - BPLOG(INFO) << "MinidumpContext unknown context type " << + // Unknown context type + BPLOG(ERROR) << "MinidumpContext unknown context type " << HexString(cpu_type); return false; break; @@ -1312,7 +1302,7 @@ void MinidumpThread::Print() { // -u_int32_t MinidumpThreadList::max_threads_ = 4096; +u_int32_t MinidumpThreadList::max_threads_ = 256; MinidumpThreadList::MinidumpThreadList(Minidump* minidump) @@ -1474,8 +1464,8 @@ void MinidumpThreadList::Print() { // -u_int32_t MinidumpModule::max_cv_bytes_ = 32768; -u_int32_t MinidumpModule::max_misc_bytes_ = 32768; +u_int32_t MinidumpModule::max_cv_bytes_ = 1024; +u_int32_t MinidumpModule::max_misc_bytes_ = 1024; MinidumpModule::MinidumpModule(Minidump* minidump) @@ -2434,7 +2424,7 @@ void MinidumpModuleList::Print() { // -u_int32_t MinidumpMemoryList::max_regions_ = 4096; +u_int32_t MinidumpMemoryList::max_regions_ = 256; MinidumpMemoryList::MinidumpMemoryList(Minidump* minidump) @@ -2723,10 +2713,8 @@ MinidumpContext* MinidumpException::GetContext() { scoped_ptr context(new MinidumpContext(minidump_)); - // Don't log as an error if we can still fall back on th thread's context - // (which must be possible if we got his far.) if (!context->Read(exception_.thread_context.data_size)) { - BPLOG(INFO) << "MinidumpException cannot read context"; + BPLOG(ERROR) << "MinidumpException cannot read context"; return NULL; } @@ -2777,109 +2765,6 @@ void MinidumpException::Print() { } } -// -// MinidumpAssertion -// - - -MinidumpAssertion::MinidumpAssertion(Minidump* minidump) - : MinidumpStream(minidump), - assertion_(), - expression_(), - function_(), - file_() { -} - - -MinidumpAssertion::~MinidumpAssertion() { -} - - -bool MinidumpAssertion::Read(u_int32_t expected_size) { - // Invalidate cached data. - valid_ = false; - - if (expected_size != sizeof(assertion_)) { - BPLOG(ERROR) << "MinidumpAssertion size mismatch, " << expected_size << - " != " << sizeof(assertion_); - return false; - } - - if (!minidump_->ReadBytes(&assertion_, sizeof(assertion_))) { - BPLOG(ERROR) << "MinidumpAssertion cannot read assertion"; - return false; - } - - // Each of {expression, function, file} is a UTF-16 string, - // we'll convert them to UTF-8 for ease of use. - // expression - // Since we don't have an explicit byte length for each string, - // we use UTF16codeunits to calculate word length, then derive byte - // length from that. - u_int32_t word_length = UTF16codeunits(assertion_.expression, - sizeof(assertion_.expression)); - if (word_length > 0) { - u_int32_t byte_length = word_length * 2; - vector expression_utf16(word_length); - memcpy(&expression_utf16[0], &assertion_.expression[0], byte_length); - - scoped_ptr new_expression(UTF16ToUTF8(expression_utf16, - minidump_->swap())); - expression_ = *new_expression; - } - - // assertion - word_length = UTF16codeunits(assertion_.function, - sizeof(assertion_.function)); - if (word_length) { - u_int32_t byte_length = word_length * 2; - vector function_utf16(word_length); - memcpy(&function_utf16[0], &assertion_.function[0], byte_length); - scoped_ptr new_function(UTF16ToUTF8(function_utf16, - minidump_->swap())); - function_ = *new_function; - } - - // file - word_length = UTF16codeunits(assertion_.file, - sizeof(assertion_.file)); - if (word_length > 0) { - u_int32_t byte_length = word_length * 2; - vector file_utf16(word_length); - memcpy(&file_utf16[0], &assertion_.file[0], byte_length); - scoped_ptr new_file(UTF16ToUTF8(file_utf16, - minidump_->swap())); - file_ = *new_file; - } - - if (minidump_->swap()) { - Swap(&assertion_.line); - Swap(&assertion_.type); - } - - valid_ = true; - return true; -} - -void MinidumpAssertion::Print() { - if (!valid_) { - BPLOG(ERROR) << "MinidumpAssertion cannot print invalid data"; - return; - } - - printf("MDAssertion\n"); - printf(" expression = %s\n", - expression_.c_str()); - printf(" function = %s\n", - function_.c_str()); - printf(" file = %s\n", - file_.c_str()); - printf(" line = %u\n", - assertion_.line); - printf(" type = %u\n", - assertion_.type); - printf("\n"); -} // // MinidumpSystemInfo @@ -3527,11 +3412,6 @@ MinidumpException* Minidump::GetException() { return GetStream(&exception); } -MinidumpAssertion* Minidump::GetAssertion() { - MinidumpAssertion* assertion; - return GetStream(&assertion); -} - MinidumpSystemInfo* Minidump::GetSystemInfo() { MinidumpSystemInfo* system_info; diff --git a/toolkit/crashreporter/google-breakpad/src/processor/minidump_dump.cc b/toolkit/crashreporter/google-breakpad/src/processor/minidump_dump.cc index b586252f453..12a699de5c4 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/minidump_dump.cc +++ b/toolkit/crashreporter/google-breakpad/src/processor/minidump_dump.cc @@ -44,7 +44,6 @@ using google_breakpad::MinidumpThreadList; using google_breakpad::MinidumpModuleList; using google_breakpad::MinidumpMemoryList; using google_breakpad::MinidumpException; -using google_breakpad::MinidumpAssertion; using google_breakpad::MinidumpSystemInfo; using google_breakpad::MinidumpMiscInfo; using google_breakpad::MinidumpBreakpadInfo; @@ -90,13 +89,6 @@ static bool PrintMinidumpDump(const char *minidump_file) { exception->Print(); } - MinidumpAssertion *assertion = minidump.GetAssertion(); - if (!assertion) { - BPLOG(INFO) << "minidump.GetAssertion() failed"; - } else { - assertion->Print(); - } - MinidumpSystemInfo *system_info = minidump.GetSystemInfo(); if (!system_info) { ++errors; diff --git a/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor.cc b/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor.cc index 24b03e9d964..5ca41655e4c 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor.cc +++ b/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor.cc @@ -28,7 +28,6 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include -#include #include "google_breakpad/processor/minidump_processor.h" #include "google_breakpad/processor/call_stack.h" @@ -86,9 +85,6 @@ ProcessResult MinidumpProcessor::Process( dump, &process_state->crash_address_); } - // This will just return an empty string if it doesn't exist. - process_state->assertion_ = GetAssertion(dump); - MinidumpModuleList *module_list = dump->GetModuleList(); // Put a copy of the module list into ProcessState object. This is not @@ -172,10 +168,8 @@ ProcessResult MinidumpProcessor::Process( // of the thread's own context. For the crashed thread, the thread's // own context is the state inside the exception handler. Using it // would not result in the expected stack trace from the time of the - // crash. If the exception context is invalid, however, we fall back - // on the thread context. - MinidumpContext *ctx = exception->GetContext(); - context = ctx ? ctx : thread->GetContext(); + // crash. + context = exception->GetContext(); } } @@ -212,7 +206,6 @@ ProcessResult MinidumpProcessor::Process( interrupted = true; } process_state->threads_.push_back(stack.release()); - process_state->thread_memory_regions_.push_back(thread_memory); } if (interrupted) { @@ -759,9 +752,6 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) { case MD_EXCEPTION_CODE_WIN_POSSIBLE_DEADLOCK: reason = "EXCEPTION_POSSIBLE_DEADLOCK"; break; - case MD_EXCEPTION_CODE_WIN_UNHANDLED_CPP_EXCEPTION: - reason = "Unhandled C++ Exception"; - break; default: BPLOG(INFO) << "Unknown exception reason " << reason; break; @@ -1009,57 +999,4 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) { return reason; } -// static -string MinidumpProcessor::GetAssertion(Minidump *dump) -{ - MinidumpAssertion *assertion = dump->GetAssertion(); - if (!assertion) - return ""; - - const MDRawAssertionInfo *raw_assertion = assertion->assertion(); - if (!raw_assertion) - return ""; - - string assertion_string; - switch (raw_assertion->type) { - case MD_ASSERTION_INFO_TYPE_INVALID_PARAMETER: - assertion_string = "Invalid parameter passed to library function"; - break; - case MD_ASSERTION_INFO_TYPE_PURE_VIRTUAL_CALL: - assertion_string = "Pure virtual function called"; - break; - default: { - char assertion_type[32]; - sprintf(assertion_type, "0x%08x", raw_assertion->type); - assertion_string = "Unknown assertion type "; - assertion_string += assertion_type; - break; - } - } - - string expression = assertion->expression(); - if (!expression.empty()) { - assertion_string.append(" " + expression); - } - - string function = assertion->function(); - if (!function.empty()) { - assertion_string.append(" in function " + function); - } - - string file = assertion->file(); - if (!file.empty()) { - assertion_string.append(", in file " + file); - } - - if (raw_assertion->line != 0) { - char assertion_line[32]; - sprintf(assertion_line, "%u", raw_assertion->line); - assertion_string.append(" at line "); - assertion_string.append(assertion_line); - } - - return assertion_string; -} - } // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk.cc b/toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk.cc index 3701c466b7b..d3a830e3c7a 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk.cc +++ b/toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk.cc @@ -160,28 +160,6 @@ static void PrintStack(const CallStack *stack, const string &cpu) { sequence = PrintRegister("edx", frame_x86->context.edx, sequence); sequence = PrintRegister("efl", frame_x86->context.eflags, sequence); } - const char *trust_name; - switch (frame_x86->trust) { - case StackFrameX86::FRAME_TRUST_NONE: - trust_name = "unknown"; - break; - case StackFrameX86::FRAME_TRUST_CONTEXT: - trust_name = "given as instruction pointer in context"; - break; - case StackFrameX86::FRAME_TRUST_CFI: - trust_name = "call frame info"; - break; - case StackFrameX86::FRAME_TRUST_CFI_SCAN: - trust_name = "call frame info with scanning"; - break; - case StackFrameX86::FRAME_TRUST_FP: - trust_name = "previous frame's frame pointer"; - break; - case StackFrameX86::FRAME_TRUST_SCAN: - trust_name = "stack scanning"; - break; - } - printf("\n Found by: %s", trust_name); } else if (cpu == "ppc") { const StackFramePPC *frame_ppc = reinterpret_cast(frame); @@ -361,11 +339,6 @@ static void PrintProcessState(const ProcessState& process_state) { printf("No crash\n"); } - string assertion = process_state.assertion(); - if (!assertion.empty()) { - printf("Assertion: %s\n", assertion.c_str()); - } - // If the thread that requested the dump is known, print it first. int requesting_thread = process_state.requesting_thread(); if (requesting_thread != -1) { @@ -418,15 +391,7 @@ static void PrintProcessStateMachineReadable(const ProcessState& process_state) StripSeparator(process_state.crash_reason()).c_str(), kOutputSeparator, process_state.crash_address(), kOutputSeparator); } else { - // print assertion info, if available, in place of crash reason, - // instead of the unhelpful "No crash" - string assertion = process_state.assertion(); - if (!assertion.empty()) { - printf("%s%c%c", StripSeparator(assertion).c_str(), - kOutputSeparator, kOutputSeparator); - } else { - printf("No crash%c%c", kOutputSeparator, kOutputSeparator); - } + printf("No crash%c%c", kOutputSeparator, kOutputSeparator); } if (requesting_thread != -1) { diff --git a/toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper_unittest.cc b/toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper_unittest.cc index 54f04b780c0..957c94f2419 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper_unittest.cc +++ b/toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper_unittest.cc @@ -27,8 +27,6 @@ // (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 - #include "processor/pathname_stripper.h" #include "processor/logging.h" diff --git a/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator-inl.h b/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator-inl.h index b08cd18293a..aa0851d97e3 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator-inl.h +++ b/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator-inl.h @@ -38,7 +38,6 @@ #define PROCESSOR_POSTFIX_EVALUATOR_INL_H__ -#include #include #include "processor/postfix_evaluator.h" diff --git a/toolkit/crashreporter/google-breakpad/src/processor/process_state.cc b/toolkit/crashreporter/google-breakpad/src/processor/process_state.cc index 1d970bad67b..934792b38d0 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/process_state.cc +++ b/toolkit/crashreporter/google-breakpad/src/processor/process_state.cc @@ -48,7 +48,6 @@ void ProcessState::Clear() { crashed_ = false; crash_reason_.clear(); crash_address_ = 0; - assertion_.clear(); requesting_thread_ = -1; for (vector::const_iterator iterator = threads_.begin(); iterator != threads_.end(); diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc index 96ee6db0483..de67bdaa517 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc +++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc @@ -189,39 +189,5 @@ Stackwalker* Stackwalker::StackwalkerForCPU( return cpu_stackwalker; } -bool Stackwalker::InstructionAddressSeemsValid(u_int64_t address) { - const CodeModule *module = modules_->GetModuleForAddress(address); - if (!module) { - // not inside any loaded module - return false; - } - - if (!resolver_ || !supplier_) { - // we don't have a resolver and or symbol supplier, - // but we're inside a known module - return true; - } - - if (!resolver_->HasModule(module->code_file())) { - string symbol_data, symbol_file; - SymbolSupplier::SymbolResult symbol_result = - supplier_->GetSymbolFile(module, system_info_, - &symbol_file, &symbol_data); - - if (symbol_result != SymbolSupplier::FOUND || - !resolver_->LoadModuleUsingMapBuffer(module->code_file(), - symbol_data)) { - // we don't have symbols, but we're inside a loaded module - return true; - } - } - - StackFrame frame; - frame.module = module; - frame.instruction = address; - resolver_->FillSourceLineInfo(&frame); - // we have symbols, so return true if inside a function - return !frame.function_name.empty(); -} } // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.cc index aeb18a9f511..e39df17f26c 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.cc +++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.cc @@ -79,7 +79,6 @@ StackFrame* StackwalkerX86::GetContextFrame() { // straight out of the CPU context structure. frame->context = *context_; frame->context_validity = StackFrameX86::CONTEXT_VALID_ALL; - frame->trust = StackFrameX86::FRAME_TRUST_CONTEXT; frame->instruction = frame->context.eip; return frame; @@ -93,7 +92,7 @@ StackFrame* StackwalkerX86::GetCallerFrame( BPLOG(ERROR) << "Can't get caller frame without memory or stack"; return NULL; } - StackFrameX86::FrameTrust trust = StackFrameX86::FRAME_TRUST_NONE; + StackFrameX86 *last_frame = static_cast( stack->frames()->back()); StackFrameInfo *last_frame_info = stack_frame_info.back().get(); @@ -184,7 +183,6 @@ StackFrame* StackwalkerX86::GetCallerFrame( if (last_frame_info && last_frame_info->valid == StackFrameInfo::VALID_ALL) { // FPO data available. traditional_frame = false; - trust = StackFrameX86::FRAME_TRUST_CFI; if (!last_frame_info->program_string.empty()) { // The FPO data has its own program string, which will tell us how to // get to the caller frame, and may even fill in the values of @@ -282,7 +280,6 @@ StackFrame* StackwalkerX86::GetCallerFrame( // %eip_new = *(%ebp_old + 4) // %esp_new = %ebp_old + 8 // %ebp_new = *(%ebp_old) - trust = StackFrameX86::FRAME_TRUST_FP; program_string = "$eip $ebp 4 + ^ = " "$esp $ebp 8 + = " "$ebp $ebp ^ ="; @@ -296,26 +293,7 @@ StackFrame* StackwalkerX86::GetCallerFrame( if (!evaluator.Evaluate(program_string, &dictionary_validity) || dictionary_validity.find("$eip") == dictionary_validity.end() || dictionary_validity.find("$esp") == dictionary_validity.end()) { - // Program string evaluation failed. It may be that %eip is not somewhere - // with stack frame info, and %ebp is pointing to non-stack memory, so - // our evaluation couldn't succeed. We'll scan the stack for a return - // address. This can happen if the stack is in a module for which - // we don't have symbols, and that module is compiled without a - // frame pointer. - u_int32_t location_start = last_frame->context.esp; - u_int32_t location, eip; - if (!ScanForReturnAddress(location_start, location, eip)) { - // if we can't find an instruction pointer even with stack scanning, - // give up. - return NULL; - } - - // This seems like a reasonable return address. Since program string - // evaluation failed, use it and set %esp to the location above the - // one where the return address was found. - dictionary["$eip"] = eip; - dictionary["$esp"] = location + 4; - trust = StackFrameX86::FRAME_TRUST_SCAN; + return NULL; } // If this stack frame did not use %ebp in a traditional way, locating the @@ -343,18 +321,33 @@ StackFrame* StackwalkerX86::GetCallerFrame( u_int32_t eip = dictionary["$eip"]; if (modules_ && !modules_->GetModuleForAddress(eip)) { + const int kRASearchWords = 15; + // The instruction pointer at .raSearchStart was invalid, so start // looking one 32-bit word above that location. u_int32_t location_start = dictionary[".raSearchStart"] + 4; - u_int32_t location; - if (ScanForReturnAddress(location_start, location, eip)) { - // This is a better return address that what program string - // evaluation found. Use it, and set %esp to the location above the - // one where the return address was found. - dictionary["$eip"] = eip; - dictionary["$esp"] = location + 4; - offset = location - location_start; - trust = StackFrameX86::FRAME_TRUST_CFI_SCAN; + + for (u_int32_t location = location_start; + location <= location_start + kRASearchWords * 4; + location += 4) { + if (!memory_->GetMemoryAtAddress(location, &eip)) + break; + + if (modules_->GetModuleForAddress(eip)) { + // This is a better return address that what program string + // evaluation found. Use it, and set %esp to the location above the + // one where the return address was found. + // + // TODO(mmentovai): The return-address check can be made even + // stronger in modules for which debugging data is available. In + // that case, it's possible to check that the candidate return + // address is inside a known function. + + dictionary["$eip"] = eip; + dictionary["$esp"] = location + 4; + offset = location - location_start; + break; + } } } @@ -399,7 +392,6 @@ StackFrame* StackwalkerX86::GetCallerFrame( // and fill it in. StackFrameX86 *frame = new StackFrameX86(); - frame->trust = trust; frame->context = last_frame->context; frame->context.eip = dictionary["$eip"]; frame->context.esp = dictionary["$esp"]; @@ -436,27 +428,5 @@ StackFrame* StackwalkerX86::GetCallerFrame( return frame; } -bool StackwalkerX86::ScanForReturnAddress(u_int32_t location_start, - u_int32_t &location_found, - u_int32_t &eip_found) { - const int kRASearchWords = 15; - for (u_int32_t location = location_start; - location <= location_start + kRASearchWords * 4; - location += 4) { - u_int32_t eip; - if (!memory_->GetMemoryAtAddress(location, &eip)) - break; - - if (modules_ && modules_->GetModuleForAddress(eip) && - InstructionAddressSeemsValid(eip)) { - - eip_found = eip; - location_found = location; - return true; - } - } - // nothing found - return false; -} } // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.h b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.h index d4137ebf776..14b9e8b9950 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.h +++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.h @@ -70,19 +70,6 @@ class StackwalkerX86 : public Stackwalker { const CallStack *stack, const vector< linked_ptr > &stack_frame_info); - // Scan the stack starting at location_start, looking for an address - // that looks like a valid instruction pointer. Addresses must - // 1) be contained in the current stack memory - // 2) pass the checks in Stackwalker::InstructionAddressSeemsValid - // - // Returns true if a valid-looking instruction pointer was found. - // When returning true, sets location_found to the address at which - // the value was found, and eip_found to the value contained at that - // location in memory. - bool ScanForReturnAddress(u_int32_t location_start, - u_int32_t &location_found, - u_int32_t &eip_found); - // Stores the CPU context corresponding to the innermost stack frame to // be returned by GetContextFrame. const MDRawContextX86 *context_; diff --git a/toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.stackwalk.out b/toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.stackwalk.out index be081f415ea..21b425de352 100644 --- a/toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.stackwalk.out +++ b/toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.stackwalk.out @@ -12,16 +12,12 @@ Thread 0 (crashed) eip = 0x0040429e esp = 0x0012fe84 ebp = 0x0012fe88 ebx = 0x7c80abc1 esi = 0x00000002 edi = 0x00000a28 eax = 0x00000045 ecx = 0x0012fe94 edx = 0x0042bc58 efl = 0x00010246 - Found by: given as instruction pointer in context 1 test_app.exe!main [test_app.cc : 65 + 0x4] eip = 0x00404200 esp = 0x0012fe90 ebp = 0x0012ff70 - Found by: call frame info 2 test_app.exe!__tmainCRTStartup [crt0.c : 327 + 0x11] eip = 0x004053ec esp = 0x0012ff78 ebp = 0x0012ffc0 - Found by: call frame info 3 kernel32.dll!BaseProcessStart + 0x22 eip = 0x7c816fd7 esp = 0x0012ffc8 ebp = 0x0012fff0 - Found by: call frame info Loaded modules: 0x00400000 - 0x0042cfff test_app.exe ??? (main) diff --git a/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags.h b/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags.h deleted file mode 100644 index 08a3b637e14..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags.h +++ /dev/null @@ -1,533 +0,0 @@ -// Copyright (c) 2006, 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. - -// --- -// Author: Ray Sidney -// Revamped and reorganized by Craig Silverstein -// -// This is the file that should be included by any file which declares -// or defines a command line flag or wants to parse command line flags -// or print a program usage message (which will include information about -// flags). Executive summary, in the form of an example foo.cc file: -// -// #include "foo.h" // foo.h has a line "DECLARE_int32(start);" -// -// DEFINE_int32(end, 1000, "The last record to read"); -// DECLARE_bool(verbose); // some other file has a DEFINE_bool(verbose, ...) -// -// void MyFunc() { -// if (FLAGS_verbose) printf("Records %d-%d\n", FLAGS_start, FLAGS_end); -// } -// -// Then, at the command-line: -// ./foo --noverbose --start=5 --end=100 -// -// For more details, see -// doc/gflags.html -// -// --- A note about thread-safety: -// -// We describe many functions in this routine as being thread-hostile, -// thread-compatible, or thread-safe. Here are the meanings we use: -// -// thread-safe: it is safe for multiple threads to call this routine -// (or, when referring to a class, methods of this class) -// concurrently. -// thread-hostile: it is not safe for multiple threads to call this -// routine (or methods of this class) concurrently. In gflags, -// most thread-hostile routines are intended to be called early in, -// or even before, main() -- that is, before threads are spawned. -// thread-compatible: it is safe for multiple threads to read from -// this variable (when applied to variables), or to call const -// methods of this class (when applied to classes), as long as no -// other thread is writing to the variable or calling non-const -// methods of this class. - -#ifndef GOOGLE_GFLAGS_H_ -#define GOOGLE_GFLAGS_H_ - -#include -#include - -// We care a lot about number of bits things take up. Unfortunately, -// systems define their bit-specific ints in a lot of different ways. -// We use our own way, and have a typedef to get there. -// Note: these commands below may look like "#if 1" or "#if 0", but -// that's because they were constructed that way at ./configure time. -// Look at gflags.h.in to see how they're calculated (based on your config). -#if 1 -#include // the normal place uint16_t is defined -#endif -#if 1 -#include // the normal place u_int16_t is defined -#endif -#if 1 -#include // a third place for uint16_t or u_int16_t -#endif - -namespace google { - -#if 1 // the C99 format -typedef int32_t int32; -typedef uint32_t uint32; -typedef int64_t int64; -typedef uint64_t uint64; -#elif 1 // the BSD format -typedef int32_t int32; -typedef u_int32_t uint32; -typedef int64_t int64; -typedef u_int64_t uint64; -#elif 0 // the windows (vc7) format -typedef __int32 int32; -typedef unsigned __int32 uint32; -typedef __int64 int64; -typedef unsigned __int64 uint64; -#else -#error Do not know how to define a 32-bit integer quantity on your system -#endif - -// -------------------------------------------------------------------- -// To actually define a flag in a file, use DEFINE_bool, -// DEFINE_string, etc. at the bottom of this file. You may also find -// it useful to register a validator with the flag. This ensures that -// when the flag is parsed from the commandline, or is later set via -// SetCommandLineOption, we call the validation function. -// -// The validation function should return true if the flag value is valid, and -// false otherwise. If the function returns false for the new setting of the -// flag, the flag will retain its current value. If it returns false for the -// default value, InitGoogle will die. -// -// This function is safe to call at global construct time (as in the -// example below). -// -// Example use: -// static bool ValidatePort(const char* flagname, int32 value) { -// if (value > 0 && value < 32768) // value is ok -// return true; -// printf("Invalid value for --%s: %d\n", flagname, (int)value); -// return false; -// } -// DEFINE_int32(port, 0, "What port to listen on"); -// static bool dummy = RegisterFlagValidator(&FLAGS_port, &ValidatePort); - -// Returns true if successfully registered, false if not (because the -// first argument doesn't point to a command-line flag, or because a -// validator is already registered for this flag). -bool RegisterFlagValidator(const bool* flag, - bool (*validate_fn)(const char*, bool)); -bool RegisterFlagValidator(const int32* flag, - bool (*validate_fn)(const char*, int32)); -bool RegisterFlagValidator(const int64* flag, - bool (*validate_fn)(const char*, int64)); -bool RegisterFlagValidator(const uint64* flag, - bool (*validate_fn)(const char*, uint64)); -bool RegisterFlagValidator(const double* flag, - bool (*validate_fn)(const char*, double)); -bool RegisterFlagValidator(const std::string* flag, - bool (*validate_fn)(const char*, const std::string&)); - - -// -------------------------------------------------------------------- -// These methods are the best way to get access to info about the -// list of commandline flags. Note that these routines are pretty slow. -// GetAllFlags: mostly-complete info about the list, sorted by file. -// ShowUsageWithFlags: pretty-prints the list to stdout (what --help does) -// ShowUsageWithFlagsRestrict: limit to filenames with restrict as a substr -// -// In addition to accessing flags, you can also access argv[0] (the program -// name) and argv (the entire commandline), which we sock away a copy of. -// These variables are static, so you should only set them once. - -struct CommandLineFlagInfo { - std::string name; // the name of the flag - std::string type; // the type of the flag: int32, etc - std::string description; // the "help text" associated with the flag - std::string current_value; // the current value, as a string - std::string default_value; // the default value, as a string - std::string filename; // 'cleaned' version of filename holding the flag - bool has_validator_fn; // true if RegisterFlagValidator called on flag - bool is_default; // true if the flag has default value -}; - -extern void GetAllFlags(std::vector* OUTPUT); -// These two are actually defined in commandlineflags_reporting.cc. -extern void ShowUsageWithFlags(const char *argv0); // what --help does -extern void ShowUsageWithFlagsRestrict(const char *argv0, const char *restrict); - -// Create a descriptive string for a flag. -// Goes to some trouble to make pretty line breaks. -extern std::string DescribeOneFlag(const CommandLineFlagInfo& flag); - -// Thread-hostile; meant to be called before any threads are spawned. -extern void SetArgv(int argc, const char** argv); -// The following functions are thread-safe as long as SetArgv() is -// only called before any threads start. -extern const std::vector& GetArgvs(); // all of argv as a vector -extern const char* GetArgv(); // all of argv as a string -extern const char* GetArgv0(); // only argv0 -extern uint32 GetArgvSum(); // simple checksum of argv -extern const char* ProgramInvocationName(); // argv0, or "UNKNOWN" if not set -extern const char* ProgramInvocationShortName(); // basename(argv0) -// ProgramUsage() is thread-safe as long as SetUsageMessage() is only -// called before any threads start. -extern const char* ProgramUsage(); // string set by SetUsageMessage() - - -// -------------------------------------------------------------------- -// Normally you access commandline flags by just saying "if (FLAGS_foo)" -// or whatever, and set them by calling "FLAGS_foo = bar" (or, more -// commonly, via the DEFINE_foo macro). But if you need a bit more -// control, we have programmatic ways to get/set the flags as well. -// These programmatic ways to access flags are thread-safe, but direct -// access is only thread-compatible. - -// Return true iff the flagname was found. -// OUTPUT is set to the flag's value, or unchanged if we return false. -extern bool GetCommandLineOption(const char* name, std::string* OUTPUT); - -// Return true iff the flagname was found. OUTPUT is set to the flag's -// CommandLineFlagInfo or unchanged if we return false. -extern bool GetCommandLineFlagInfo(const char* name, - CommandLineFlagInfo* OUTPUT); - -// Return the CommandLineFlagInfo of the flagname. exit() if name not found. -// Example usage, to check if a flag's value is currently the default value: -// if (GetCommandLineFlagInfoOrDie("foo").is_default) ... -extern CommandLineFlagInfo GetCommandLineFlagInfoOrDie(const char* name); - -enum FlagSettingMode { - // update the flag's value (can call this multiple times). - SET_FLAGS_VALUE, - // update the flag's value, but *only if* it has not yet been updated - // with SET_FLAGS_VALUE, SET_FLAG_IF_DEFAULT, or "FLAGS_xxx = nondef". - SET_FLAG_IF_DEFAULT, - // set the flag's default value to this. If the flag has not yet updated - // yet (via SET_FLAGS_VALUE, SET_FLAG_IF_DEFAULT, or "FLAGS_xxx = nondef") - // change the flag's current value to the new default value as well. - SET_FLAGS_DEFAULT -}; - -// Set a particular flag ("command line option"). Returns a string -// describing the new value that the option has been set to. The -// return value API is not well-specified, so basically just depend on -// it to be empty if the setting failed for some reason -- the name is -// not a valid flag name, or the value is not a valid value -- and -// non-empty else. - -// SetCommandLineOption uses set_mode == SET_FLAGS_VALUE (the common case) -extern std::string SetCommandLineOption(const char* name, const char* value); -extern std::string SetCommandLineOptionWithMode(const char* name, const char* value, - FlagSettingMode set_mode); - - -// -------------------------------------------------------------------- -// Saves the states (value, default value, whether the user has set -// the flag, registered validators, etc) of all flags, and restores -// them when the FlagSaver is destroyed. This is very useful in -// tests, say, when you want to let your tests change the flags, but -// make sure that they get reverted to the original states when your -// test is complete. -// -// Example usage: -// void TestFoo() { -// FlagSaver s1; -// FLAG_foo = false; -// FLAG_bar = "some value"; -// -// // test happens here. You can return at any time -// // without worrying about restoring the FLAG values. -// } -// -// Note: This class is marked with __attribute__((unused)) because all the -// work is done in the constructor and destructor, so in the standard -// usage example above, the compiler would complain that it's an -// unused variable. -// -// This class is thread-safe. - -class FlagSaver { - public: - FlagSaver(); - ~FlagSaver(); - - private: - class FlagSaverImpl* impl_; // we use pimpl here to keep API steady - - FlagSaver(const FlagSaver&); // no copying! - void operator=(const FlagSaver&); -} __attribute__ ((unused)); - -// -------------------------------------------------------------------- -// Some deprecated or hopefully-soon-to-be-deprecated functions. - -// This is often used for logging. TODO(csilvers): figure out a better way -extern std::string CommandlineFlagsIntoString(); -// Usually where this is used, a FlagSaver should be used instead. -extern bool ReadFlagsFromString(const std::string& flagfilecontents, - const char* prog_name, - bool errors_are_fatal); // uses SET_FLAGS_VALUE - -// These let you manually implement --flagfile functionality. -// DEPRECATED. -extern bool AppendFlagsIntoFile(const std::string& filename, const char* prog_name); -extern bool SaveCommandFlags(); // actually defined in google.cc ! -extern bool ReadFromFlagsFile(const std::string& filename, const char* prog_name, - bool errors_are_fatal); // uses SET_FLAGS_VALUE - - -// -------------------------------------------------------------------- -// Useful routines for initializing flags from the environment. -// In each case, if 'varname' does not exist in the environment -// return defval. If 'varname' does exist but is not valid -// (e.g., not a number for an int32 flag), abort with an error. -// Otherwise, return the value. NOTE: for booleans, for true use -// 't' or 'T' or 'true' or '1', for false 'f' or 'F' or 'false' or '0'. - -extern bool BoolFromEnv(const char *varname, bool defval); -extern int32 Int32FromEnv(const char *varname, int32 defval); -extern int64 Int64FromEnv(const char *varname, int64 defval); -extern uint64 Uint64FromEnv(const char *varname, uint64 defval); -extern double DoubleFromEnv(const char *varname, double defval); -extern const char *StringFromEnv(const char *varname, const char *defval); - - -// -------------------------------------------------------------------- -// The next two functions parse commandlineflags from main(): - -// Set the "usage" message for this program. For example: -// string usage("This program does nothing. Sample usage:\n"); -// usage += argv[0] + " "; -// SetUsageMessage(usage); -// Do not include commandline flags in the usage: we do that for you! -// Thread-hostile; meant to be called before any threads are spawned. -extern void SetUsageMessage(const std::string& usage); - -// Looks for flags in argv and parses them. Rearranges argv to put -// flags first, or removes them entirely if remove_flags is true. -// If a flag is defined more than once in the command line or flag -// file, the last definition is used. -// See top-of-file for more details on this function. -#ifndef SWIG // In swig, use ParseCommandLineFlagsScript() instead. -extern uint32 ParseCommandLineFlags(int *argc, char*** argv, - bool remove_flags); -#endif - - -// Calls to ParseCommandLineNonHelpFlags and then to -// HandleCommandLineHelpFlags can be used instead of a call to -// ParseCommandLineFlags during initialization, in order to allow for -// changing default values for some FLAGS (via -// e.g. SetCommandLineOptionWithMode calls) between the time of -// command line parsing and the time of dumping help information for -// the flags as a result of command line parsing. -// If a flag is defined more than once in the command line or flag -// file, the last definition is used. -extern uint32 ParseCommandLineNonHelpFlags(int *argc, char*** argv, - bool remove_flags); -// This is actually defined in commandlineflags_reporting.cc. -// This function is misnamed (it also handles --version, etc.), but -// it's too late to change that now. :-( -extern void HandleCommandLineHelpFlags(); // in commandlineflags_reporting.cc - -// Allow command line reparsing. Disables the error normally -// generated when an unknown flag is found, since it may be found in a -// later parse. Thread-hostile; meant to be called before any threads -// are spawned. -extern void AllowCommandLineReparsing(); - -// Reparse the flags that have not yet been recognized. -// Only flags registered since the last parse will be recognized. -// Any flag value must be provided as part of the argument using "=", -// not as a separate command line argument that follows the flag argument. -// Intended for handling flags from dynamically loaded libraries, -// since their flags are not registered until they are loaded. -extern uint32 ReparseCommandLineNonHelpFlags(); - - -// -------------------------------------------------------------------- -// Now come the command line flag declaration/definition macros that -// will actually be used. They're kind of hairy. A major reason -// for this is initialization: we want people to be able to access -// variables in global constructors and have that not crash, even if -// their global constructor runs before the global constructor here. -// (Obviously, we can't guarantee the flags will have the correct -// default value in that case, but at least accessing them is safe.) -// The only way to do that is have flags point to a static buffer. -// So we make one, using a union to ensure proper alignment, and -// then use placement-new to actually set up the flag with the -// correct default value. In the same vein, we have to worry about -// flag access in global destructors, so FlagRegisterer has to be -// careful never to destroy the flag-values it constructs. -// -// Note that when we define a flag variable FLAGS_, we also -// preemptively define a junk variable, FLAGS_no. This is to -// cause a link-time error if someone tries to define 2 flags with -// names like "logging" and "nologging". We do this because a bool -// flag FLAG can be set from the command line to true with a "-FLAG" -// argument, and to false with a "-noFLAG" argument, and so this can -// potentially avert confusion. -// -// We also put flags into their own namespace. It is purposefully -// named in an opaque way that people should have trouble typing -// directly. The idea is that DEFINE puts the flag in the weird -// namespace, and DECLARE imports the flag from there into the current -// namespace. The net result is to force people to use DECLARE to get -// access to a flag, rather than saying "extern bool FLAGS_whatever;" -// or some such instead. We want this so we can put extra -// functionality (like sanity-checking) in DECLARE if we want, and -// make sure it is picked up everywhere. -// -// We also put the type of the variable in the namespace, so that -// people can't DECLARE_int32 something that they DEFINE_bool'd -// elsewhere. - -class FlagRegisterer { - public: - FlagRegisterer(const char* name, const char* type, - const char* help, const char* filename, - void* current_storage, void* defvalue_storage); -}; - -extern bool FlagsTypeWarn(const char *name); - -// If your application #defines STRIP_FLAG_HELP to a non-zero value -// before #including this file, we remove the help message from the -// binary file. This can reduce the size of the resulting binary -// somewhat, and may also be useful for security reasons. - -extern const char kStrippedFlagHelp[]; - -} - -#ifndef SWIG // In swig, ignore the main flag declarations - -#if defined(STRIP_FLAG_HELP) && STRIP_FLAG_HELP > 0 -// Need this construct to avoid the 'defined but not used' warning. -#define MAYBE_STRIPPED_HELP(txt) (false ? (txt) : kStrippedFlagHelp) -#else -#define MAYBE_STRIPPED_HELP(txt) txt -#endif - -// Each command-line flag has two variables associated with it: one -// with the current value, and one with the default value. However, -// we have a third variable, which is where value is assigned; it's a -// constant. This guarantees that FLAG_##value is initialized at -// static initialization time (e.g. before program-start) rather than -// than global construction time (which is after program-start but -// before main), at least when 'value' is a compile-time constant. We -// use a small trick for the "default value" variable, and call it -// FLAGS_no. This serves the second purpose of assuring a -// compile error if someone tries to define a flag named no -// which is illegal (--foo and --nofoo both affect the "foo" flag). -#define DEFINE_VARIABLE(type, shorttype, name, value, help) \ - namespace fL##shorttype { \ - static const type FLAGS_nono##name = value; \ - type FLAGS_##name = FLAGS_nono##name; \ - type FLAGS_no##name = FLAGS_nono##name; \ - static ::google::FlagRegisterer o_##name( \ - #name, #type, MAYBE_STRIPPED_HELP(help), __FILE__, \ - &FLAGS_##name, &FLAGS_no##name); \ - } \ - using fL##shorttype::FLAGS_##name - -#define DECLARE_VARIABLE(type, shorttype, name) \ - namespace fL##shorttype { \ - extern type FLAGS_##name; \ - } \ - using fL##shorttype::FLAGS_##name - -// For DEFINE_bool, we want to do the extra check that the passed-in -// value is actually a bool, and not a string or something that can be -// coerced to a bool. These declarations (no definition needed!) will -// help us do that, and never evaluate From, which is important. -// We'll use 'sizeof(IsBool(val))' to distinguish. This code requires -// that the compiler have different sizes for bool & double. Since -// this is not guaranteed by the standard, we check it with a -// compile-time assert (msg[-1] will give a compile-time error). -namespace fLB { -struct CompileAssert {}; -typedef CompileAssert expected_sizeof_double_neq_sizeof_bool[ - (sizeof(double) != sizeof(bool)) ? 1 : -1]; -template double IsBoolFlag(const From& from); -bool IsBoolFlag(bool from); -} // namespace fLB - -#define DECLARE_bool(name) DECLARE_VARIABLE(bool,B, name) -#define DEFINE_bool(name,val,txt) \ - namespace fLB { \ - typedef CompileAssert FLAG_##name##_value_is_not_a_bool[ \ - (sizeof(::fLB::IsBoolFlag(val)) != sizeof(double)) ? 1 : -1]; \ - } \ - DEFINE_VARIABLE(bool,B, name, val, txt) - -#define DECLARE_int32(name) DECLARE_VARIABLE(::google::int32,I, name) -#define DEFINE_int32(name,val,txt) DEFINE_VARIABLE(::google::int32,I, name, val, txt) - -#define DECLARE_int64(name) DECLARE_VARIABLE(::google::int64,I64, name) -#define DEFINE_int64(name,val,txt) DEFINE_VARIABLE(::google::int64,I64, name, val, txt) - -#define DECLARE_uint64(name) DECLARE_VARIABLE(::google::uint64,U64, name) -#define DEFINE_uint64(name,val,txt) DEFINE_VARIABLE(::google::uint64,U64, name, val, txt) - -#define DECLARE_double(name) DECLARE_VARIABLE(double,D, name) -#define DEFINE_double(name,val,txt) DEFINE_VARIABLE(double,D, name, val, txt) - -// Strings are trickier, because they're not a POD, so we can't -// construct them at static-initialization time (instead they get -// constructed at global-constructor time, which is much later). To -// try to avoid crashes in that case, we use a char buffer to store -// the string, which we can static-initialize, and then placement-new -// into it later. It's not perfect, but the best we can do. -#define DECLARE_string(name) namespace fLS { extern std::string& FLAGS_##name; } \ - using fLS::FLAGS_##name - -// We need to define a var named FLAGS_no##name so people don't define -// --string and --nostring. And we need a temporary place to put val -// so we don't have to evaluate it twice. Two great needs that go -// great together! -// The weird 'using' + 'extern' inside the fLS namespace is to work around -// an unknown compiler bug/issue with the gcc 4.2.1 on SUSE 10. See -// http://code.google.com/p/google-gflags/issues/detail?id=20 -#define DEFINE_string(name, val, txt) \ - namespace fLS { \ - static union { void* align; char s[sizeof(std::string)]; } s_##name[2]; \ - const std::string* const FLAGS_no##name = new (s_##name[0].s) std::string(val); \ - static ::google::FlagRegisterer o_##name( \ - #name, "string", MAYBE_STRIPPED_HELP(txt), __FILE__, \ - s_##name[0].s, new (s_##name[1].s) std::string(*FLAGS_no##name)); \ - extern std::string& FLAGS_##name; \ - using fLS::FLAGS_##name; \ - std::string& FLAGS_##name = *(reinterpret_cast(s_##name[0].s)); \ - } \ - using fLS::FLAGS_##name - -#endif // SWIG - -#endif // GOOGLE_GFLAGS_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags_completions.h b/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags_completions.h deleted file mode 100644 index 9d9ce7a5f75..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags_completions.h +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) 2008, 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. -// -// --- -// Author: Dave Nicponski -// -// Implement helpful bash-style command line flag completions -// -// ** Functional API: -// HandleCommandLineCompletions() should be called early during -// program startup, but after command line flag code has been -// initialized, such as the beginning of HandleCommandLineHelpFlags(). -// It checks the value of the flag --tab_completion_word. If this -// flag is empty, nothing happens here. If it contains a string, -// however, then HandleCommandLineCompletions() will hijack the -// process, attempting to identify the intention behind this -// completion. Regardless of the outcome of this deduction, the -// process will be terminated, similar to --helpshort flag -// handling. -// -// ** Overview of Bash completions: -// Bash can be told to programatically determine completions for the -// current 'cursor word'. It does this by (in this case) invoking a -// command with some additional arguments identifying the command -// being executed, the word being completed, and the previous word -// (if any). Bash then expects a sequence of output lines to be -// printed to stdout. If these lines all contain a common prefix -// longer than the cursor word, bash will replace the cursor word -// with that common prefix, and display nothing. If there isn't such -// a common prefix, bash will display the lines in pages using 'more'. -// -// ** Strategy taken for command line completions: -// If we can deduce either the exact flag intended, or a common flag -// prefix, we'll output exactly that. Otherwise, if information -// must be displayed to the user, we'll take the opportunity to add -// some helpful information beyond just the flag name (specifically, -// we'll include the default flag value and as much of the flag's -// description as can fit on a single terminal line width, as specified -// by the flag --tab_completion_columns). Furthermore, we'll try to -// make bash order the output such that the most useful or relevent -// flags are the most likely to be shown at the top. -// -// ** Additional features: -// To assist in finding that one really useful flag, substring matching -// was implemented. Before pressing a to get completion for the -// current word, you can append one or more '?' to the flag to do -// substring matching. Here's the semantics: -// --foo Show me all flags with names prefixed by 'foo' -// --foo? Show me all flags with 'foo' somewhere in the name -// --foo?? Same as prior case, but also search in module -// definition path for 'foo' -// --foo??? Same as prior case, but also search in flag -// descriptions for 'foo' -// Finally, we'll trim the output to a relatively small number of -// flags to keep bash quiet about the verbosity of output. If one -// really wanted to see all possible matches, appending a '+' to the -// search word will force the exhaustive list of matches to be printed. -// -// ** How to have bash accept completions from a binary: -// Bash requires that it be informed about each command that programmatic -// completion should be enabled for. Example addition to a .bashrc -// file would be (your path to gflags_completions.sh file may differ): - -/* -$ complete -o bashdefault -o default -o nospace -C \ - '/usr/local/bin/gflags_completions.sh --tab_completion_columns $COLUMNS' \ - time env binary_name another_binary [...] -*/ - -// This would allow the following to work: -// $ /path/to/binary_name --vmodule -// Or: -// $ ./bin/path/another_binary --gfs_u -// (etc) -// -// Sadly, it appears that bash gives no easy way to force this behavior for -// all commands. That's where the "time" in the above example comes in. -// If you haven't specifically added a command to the list of completion -// supported commands, you can still get completions by prefixing the -// entire command with "env". -// $ env /some/brand/new/binary --vmod -// Assuming that "binary" is a newly compiled binary, this should still -// produce the expected completion output. - - -#ifndef GOOGLE_GFLAGS_COMPLETIONS_H_ -#define GOOGLE_GFLAGS_COMPLETIONS_H_ - -namespace google { - -void HandleCommandLineCompletions(void); - -} - -#endif // GOOGLE_GFLAGS_COMPLETIONS_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/log_severity.h b/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/log_severity.h deleted file mode 100644 index 17805fbadd4..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/log_severity.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2007, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef BASE_LOG_SEVERITY_H__ -#define BASE_LOG_SEVERITY_H__ - -// Annoying stuff for windows -- makes sure clients can import these functions -#ifndef GOOGLE_GLOG_DLL_DECL -# if defined(_WIN32) && !defined(__CYGWIN__) -# define GOOGLE_GLOG_DLL_DECL __declspec(dllimport) -# else -# define GOOGLE_GLOG_DLL_DECL -# endif -#endif - -// Variables of type LogSeverity are widely taken to lie in the range -// [0, NUM_SEVERITIES-1]. Be careful to preserve this assumption if -// you ever need to change their values or add a new severity. -typedef int LogSeverity; - -const int INFO = 0, WARNING = 1, ERROR = 2, FATAL = 3, NUM_SEVERITIES = 4; - -// DFATAL is FATAL in debug mode, ERROR in normal mode -#ifdef NDEBUG -#define DFATAL_LEVEL ERROR -#else -#define DFATAL_LEVEL FATAL -#endif - -extern GOOGLE_GLOG_DLL_DECL const char* const LogSeverityNames[NUM_SEVERITIES]; - -// NDEBUG usage helpers related to (RAW_)DCHECK: -// -// DEBUG_MODE is for small !NDEBUG uses like -// if (DEBUG_MODE) foo.CheckThatFoo(); -// instead of substantially more verbose -// #ifndef NDEBUG -// foo.CheckThatFoo(); -// #endif -// -// IF_DEBUG_MODE is for small !NDEBUG uses like -// IF_DEBUG_MODE( string error; ) -// DCHECK(Foo(&error)) << error; -// instead of substantially more verbose -// #ifndef NDEBUG -// string error; -// DCHECK(Foo(&error)) << error; -// #endif -// -#ifdef NDEBUG -enum { DEBUG_MODE = 0 }; -#define IF_DEBUG_MODE(x) -#else -enum { DEBUG_MODE = 1 }; -#define IF_DEBUG_MODE(x) x -#endif - -#endif // BASE_LOG_SEVERITY_H__ diff --git a/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/logging.h b/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/logging.h deleted file mode 100644 index 718c97169d8..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/logging.h +++ /dev/null @@ -1,1499 +0,0 @@ -// Copyright (c) 1999, 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. -// -// Author: Ray Sidney -// -// This file contains #include information about logging-related stuff. -// Pretty much everybody needs to #include this file so that they can -// log various happenings. -// -#ifndef _LOGGING_H_ -#define _LOGGING_H_ - -#include -#include -#include -#include -#if 1 -# include -#endif -#ifdef __DEPRECATED -// Make GCC quiet. -# undef __DEPRECATED -# include -# define __DEPRECATED -#else -# include -#endif -#include - -// Annoying stuff for windows -- makes sure clients can import these functions -#ifndef GOOGLE_GLOG_DLL_DECL -# if defined(_WIN32) && !defined(__CYGWIN__) -# define GOOGLE_GLOG_DLL_DECL __declspec(dllimport) -# else -# define GOOGLE_GLOG_DLL_DECL -# endif -#endif - -// We care a lot about number of bits things take up. Unfortunately, -// systems define their bit-specific ints in a lot of different ways. -// We use our own way, and have a typedef to get there. -// Note: these commands below may look like "#if 1" or "#if 0", but -// that's because they were constructed that way at ./configure time. -// Look at logging.h.in to see how they're calculated (based on your config). -#if 1 -#include // the normal place uint16_t is defined -#endif -#if 1 -#include // the normal place u_int16_t is defined -#endif -#if 1 -#include // a third place for uint16_t or u_int16_t -#endif - -#if 0 -#include -#endif - -namespace google { - -#if 1 // the C99 format -typedef int32_t int32; -typedef uint32_t uint32; -typedef int64_t int64; -typedef uint64_t uint64; -#elif 1 // the BSD format -typedef int32_t int32; -typedef u_int32_t uint32; -typedef int64_t int64; -typedef u_int64_t uint64; -#elif 0 // the windows (vc7) format -typedef __int32 int32; -typedef unsigned __int32 uint32; -typedef __int64 int64; -typedef unsigned __int64 uint64; -#else -#error Do not know how to define a 32-bit integer quantity on your system -#endif - -} - -// The global value of GOOGLE_STRIP_LOG. All the messages logged to -// LOG(XXX) with severity less than GOOGLE_STRIP_LOG will not be displayed. -// If it can be determined at compile time that the message will not be -// printed, the statement will be compiled out. -// -// Example: to strip out all INFO and WARNING messages, use the value -// of 2 below. To make an exception for WARNING messages from a single -// file, add "#define GOOGLE_STRIP_LOG 1" to that file _before_ including -// base/logging.h -#ifndef GOOGLE_STRIP_LOG -#define GOOGLE_STRIP_LOG 0 -#endif - -// GCC can be told that a certain branch is not likely to be taken (for -// instance, a CHECK failure), and use that information in static analysis. -// Giving it this information can help it optimize for the common case in -// the absence of better information (ie. -fprofile-arcs). -// -#ifndef GOOGLE_PREDICT_BRANCH_NOT_TAKEN -#if 1 -#define GOOGLE_PREDICT_BRANCH_NOT_TAKEN(x) (__builtin_expect(x, 0)) -#else -#define GOOGLE_PREDICT_BRANCH_NOT_TAKEN(x) x -#endif -#endif - -// Make a bunch of macros for logging. The way to log things is to stream -// things to LOG(). E.g., -// -// LOG(INFO) << "Found " << num_cookies << " cookies"; -// -// You can capture log messages in a string, rather than reporting them -// immediately: -// -// vector errors; -// LOG_STRING(ERROR, &errors) << "Couldn't parse cookie #" << cookie_num; -// -// This pushes back the new error onto 'errors'; if given a NULL pointer, -// it reports the error via LOG(ERROR). -// -// You can also do conditional logging: -// -// LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; -// -// You can also do occasional logging (log every n'th occurrence of an -// event): -// -// LOG_EVERY_N(INFO, 10) << "Got the " << COUNTER << "th cookie"; -// -// The above will cause log messages to be output on the 1st, 11th, 21st, ... -// times it is executed. Note that the special COUNTER value is used to -// identify which repetition is happening. -// -// You can also do occasional conditional logging (log every n'th -// occurrence of an event, when condition is satisfied): -// -// LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Got the " << COUNTER -// << "th big cookie"; -// -// You can log messages the first N times your code executes a line. E.g. -// -// LOG_FIRST_N(INFO, 20) << "Got the " << COUNTER << "th cookie"; -// -// Outputs log messages for the first 20 times it is executed. -// -// Analogous SYSLOG, SYSLOG_IF, and SYSLOG_EVERY_N macros are available. -// These log to syslog as well as to the normal logs. If you use these at -// all, you need to be aware that syslog can drastically reduce performance, -// especially if it is configured for remote logging! Don't use these -// unless you fully understand this and have a concrete need to use them. -// Even then, try to minimize your use of them. -// -// There are also "debug mode" logging macros like the ones above: -// -// DLOG(INFO) << "Found cookies"; -// -// DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; -// -// DLOG_EVERY_N(INFO, 10) << "Got the " << COUNTER << "th cookie"; -// -// All "debug mode" logging is compiled away to nothing for non-debug mode -// compiles. -// -// We also have -// -// LOG_ASSERT(assertion); -// DLOG_ASSERT(assertion); -// -// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion; -// -// There are "verbose level" logging macros. They look like -// -// VLOG(1) << "I'm printed when you run the program with --v=1 or more"; -// VLOG(2) << "I'm printed when you run the program with --v=2 or more"; -// -// These always log at the INFO log level (when they log at all). -// The verbose logging can also be turned on module-by-module. For instance, -// --vmodule=mapreduce=2,file=1,gfs*=3 --v=0 -// will cause: -// a. VLOG(2) and lower messages to be printed from mapreduce.{h,cc} -// b. VLOG(1) and lower messages to be printed from file.{h,cc} -// c. VLOG(3) and lower messages to be printed from files prefixed with "gfs" -// d. VLOG(0) and lower messages to be printed from elsewhere -// -// The wildcarding functionality shown by (c) supports both '*' (match -// 0 or more characters) and '?' (match any single character) wildcards. -// -// There's also VLOG_IS_ON(n) "verbose level" condition macro. To be used as -// -// if (VLOG_IS_ON(2)) { -// // do some logging preparation and logging -// // that can't be accomplished with just VLOG(2) << ...; -// } -// -// There are also VLOG_IF, VLOG_EVERY_N and VLOG_IF_EVERY_N "verbose level" -// condition macros for sample cases, when some extra computation and -// preparation for logs is not needed. -// VLOG_IF(1, (size > 1024)) -// << "I'm printed when size is more than 1024 and when you run the " -// "program with --v=1 or more"; -// VLOG_EVERY_N(1, 10) -// << "I'm printed every 10th occurrence, and when you run the program " -// "with --v=1 or more. Present occurence is " << COUNTER; -// VLOG_IF_EVERY_N(1, (size > 1024), 10) -// << "I'm printed on every 10th occurence of case when size is more " -// " than 1024, when you run the program with --v=1 or more. "; -// "Present occurence is " << COUNTER; -// -// The supported severity levels for macros that allow you to specify one -// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL. -// Note that messages of a given severity are logged not only in the -// logfile for that severity, but also in all logfiles of lower severity. -// E.g., a message of severity FATAL will be logged to the logfiles of -// severity FATAL, ERROR, WARNING, and INFO. -// -// There is also the special severity of DFATAL, which logs FATAL in -// debug mode, ERROR in normal mode. -// -// Very important: logging a message at the FATAL severity level causes -// the program to terminate (after the message is logged). -// -// Unless otherwise specified, logs will be written to the filename -// "...log..", followed -// by the date, time, and pid (you can't prevent the date, time, and pid -// from being in the filename). -// -// The logging code takes two flags: -// --v=# set the verbose level -// --logtostderr log all the messages to stderr instead of to logfiles - -// LOG LINE PREFIX FORMAT -// -// Log lines have this form: -// -// Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg... -// -// where the fields are defined as follows: -// -// L A single character, representing the log level -// (eg 'I' for INFO) -// mm The month (zero padded; ie May is '05') -// dd The day (zero padded) -// hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds -// threadid The space-padded thread ID as returned by GetTID() -// (this matches the PID on Linux) -// file The file name -// line The line number -// msg The user-supplied message -// -// Example: -// -// I1103 11:57:31.739339 24395 google.cc:2341] Command line: ./some_prog -// I1103 11:57:31.739403 24395 google.cc:2342] Process id 24395 -// -// NOTE: although the microseconds are useful for comparing events on -// a single machine, clocks on different machines may not be well -// synchronized. Hence, use caution when comparing the low bits of -// timestamps from different machines. - -#ifndef DECLARE_VARIABLE -#define MUST_UNDEF_GFLAGS_DECLARE_MACROS -#define DECLARE_VARIABLE(type, name, tn) \ - namespace FLAG__namespace_do_not_use_directly_use_DECLARE_##tn##_instead { \ - extern GOOGLE_GLOG_DLL_DECL type FLAGS_##name; \ - } \ - using FLAG__namespace_do_not_use_directly_use_DECLARE_##tn##_instead::FLAGS_##name - -// bool specialization -#define DECLARE_bool(name) \ - DECLARE_VARIABLE(bool, name, bool) - -// int32 specialization -#define DECLARE_int32(name) \ - DECLARE_VARIABLE(google::int32, name, int32) - -// Special case for string, because we have to specify the namespace -// std::string, which doesn't play nicely with our FLAG__namespace hackery. -#define DECLARE_string(name) \ - namespace FLAG__namespace_do_not_use_directly_use_DECLARE_string_instead { \ - extern GOOGLE_GLOG_DLL_DECL std::string FLAGS_##name; \ - } \ - using FLAG__namespace_do_not_use_directly_use_DECLARE_string_instead::FLAGS_##name -#endif - -// Set whether log messages go to stderr instead of logfiles -DECLARE_bool(logtostderr); - -// Set whether log messages go to stderr in addition to logfiles. -DECLARE_bool(alsologtostderr); - -// Log messages at a level >= this flag are automatically sent to -// stderr in addition to log files. -DECLARE_int32(stderrthreshold); - -// Set whether the log prefix should be prepended to each line of output. -DECLARE_bool(log_prefix); - -// Log messages at a level <= this flag are buffered. -// Log messages at a higher level are flushed immediately. -DECLARE_int32(logbuflevel); - -// Sets the maximum number of seconds which logs may be buffered for. -DECLARE_int32(logbufsecs); - -// Log suppression level: messages logged at a lower level than this -// are suppressed. -DECLARE_int32(minloglevel); - -// If specified, logfiles are written into this directory instead of the -// default logging directory. -DECLARE_string(log_dir); - -// Sets the path of the directory into which to put additional links -// to the log files. -DECLARE_string(log_link); - -DECLARE_int32(v); // in vlog_is_on.cc - -// Sets the maximum log file size (in MB). -DECLARE_int32(max_log_size); - -// Sets whether to avoid logging to the disk if the disk is full. -DECLARE_bool(stop_logging_if_full_disk); - -#ifdef MUST_UNDEF_GFLAGS_DECLARE_MACROS -#undef MUST_UNDEF_GFLAGS_DECLARE_MACROS -#undef DECLARE_VARIABLE -#undef DECLARE_bool -#undef DECLARE_int32 -#undef DECLARE_string -#endif - -// Log messages below the GOOGLE_STRIP_LOG level will be compiled away for -// security reasons. See LOG(severtiy) below. - -// A few definitions of macros that don't generate much code. Since -// LOG(INFO) and its ilk are used all over our code, it's -// better to have compact code for these operations. - -#if GOOGLE_STRIP_LOG == 0 -#define COMPACT_GOOGLE_LOG_INFO google::LogMessage( \ - __FILE__, __LINE__) -#define LOG_TO_STRING_INFO(message) google::LogMessage( \ - __FILE__, __LINE__, google::INFO, message) -#else -#define COMPACT_GOOGLE_LOG_INFO google::NullStream() -#define LOG_TO_STRING_INFO(message) google::NullStream() -#endif - -#if GOOGLE_STRIP_LOG <= 1 -#define COMPACT_GOOGLE_LOG_WARNING google::LogMessage( \ - __FILE__, __LINE__, google::WARNING) -#define LOG_TO_STRING_WARNING(message) google::LogMessage( \ - __FILE__, __LINE__, google::WARNING, message) -#else -#define COMPACT_GOOGLE_LOG_WARNING google::NullStream() -#define LOG_TO_STRING_WARNING(message) google::NullStream() -#endif - -#if GOOGLE_STRIP_LOG <= 2 -#define COMPACT_GOOGLE_LOG_ERROR google::LogMessage( \ - __FILE__, __LINE__, google::ERROR) -#define LOG_TO_STRING_ERROR(message) google::LogMessage( \ - __FILE__, __LINE__, google::ERROR, message) -#else -#define COMPACT_GOOGLE_LOG_ERROR google::NullStream() -#define LOG_TO_STRING_ERROR(message) google::NullStream() -#endif - -#if GOOGLE_STRIP_LOG <= 3 -#define COMPACT_GOOGLE_LOG_FATAL google::LogMessageFatal( \ - __FILE__, __LINE__) -#define LOG_TO_STRING_FATAL(message) google::LogMessage( \ - __FILE__, __LINE__, google::FATAL, message) -#else -#define COMPACT_GOOGLE_LOG_FATAL google::NullStreamFatal() -#define LOG_TO_STRING_FATAL(message) google::NullStreamFatal() -#endif - -// For DFATAL, we want to use LogMessage (as opposed to -// LogMessageFatal), to be consistent with the original behavior. -#ifdef NDEBUG -#define COMPACT_GOOGLE_LOG_DFATAL COMPACT_GOOGLE_LOG_ERROR -#elif GOOGLE_STRIP_LOG <= 3 -#define COMPACT_GOOGLE_LOG_DFATAL LogMessage( \ - __FILE__, __LINE__, google::FATAL) -#else -#define COMPACT_GOOGLE_LOG_DFATAL google::NullStreamFatal() -#endif - -#define GOOGLE_LOG_INFO(counter) google::LogMessage(__FILE__, __LINE__, google::INFO, counter, &google::LogMessage::SendToLog) -#define SYSLOG_INFO(counter) \ - google::LogMessage(__FILE__, __LINE__, google::INFO, counter, \ - &google::LogMessage::SendToSyslogAndLog) -#define GOOGLE_LOG_WARNING(counter) \ - google::LogMessage(__FILE__, __LINE__, google::WARNING, counter, \ - &google::LogMessage::SendToLog) -#define SYSLOG_WARNING(counter) \ - google::LogMessage(__FILE__, __LINE__, google::WARNING, counter, \ - &google::LogMessage::SendToSyslogAndLog) -#define GOOGLE_LOG_ERROR(counter) \ - google::LogMessage(__FILE__, __LINE__, google::ERROR, counter, \ - &google::LogMessage::SendToLog) -#define SYSLOG_ERROR(counter) \ - google::LogMessage(__FILE__, __LINE__, google::ERROR, counter, \ - &google::LogMessage::SendToSyslogAndLog) -#define GOOGLE_LOG_FATAL(counter) \ - google::LogMessage(__FILE__, __LINE__, google::FATAL, counter, \ - &google::LogMessage::SendToLog) -#define SYSLOG_FATAL(counter) \ - google::LogMessage(__FILE__, __LINE__, google::FATAL, counter, \ - &google::LogMessage::SendToSyslogAndLog) -#define GOOGLE_LOG_DFATAL(counter) \ - google::LogMessage(__FILE__, __LINE__, google::DFATAL_LEVEL, counter, \ - &google::LogMessage::SendToLog) -#define SYSLOG_DFATAL(counter) \ - google::LogMessage(__FILE__, __LINE__, google::DFATAL_LEVEL, counter, \ - &google::LogMessage::SendToSyslogAndLog) - -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) || defined(__CYGWIN32__) -// A very useful logging macro to log windows errors: -#define LOG_SYSRESULT(result) \ - if (FAILED(result)) { \ - LPTSTR message = NULL; \ - LPTSTR msg = reinterpret_cast(&message); \ - DWORD message_length = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | \ - FORMAT_MESSAGE_FROM_SYSTEM, \ - 0, result, 0, msg, 100, NULL); \ - if (message_length > 0) { \ - google::LogMessage(__FILE__, __LINE__, ERROR, 0, \ - &google::LogMessage::SendToLog).stream() << message; \ - LocalFree(message); \ - } \ - } -#endif - -// We use the preprocessor's merging operator, "##", so that, e.g., -// LOG(INFO) becomes the token GOOGLE_LOG_INFO. There's some funny -// subtle difference between ostream member streaming functions (e.g., -// ostream::operator<<(int) and ostream non-member streaming functions -// (e.g., ::operator<<(ostream&, string&): it turns out that it's -// impossible to stream something like a string directly to an unnamed -// ostream. We employ a neat hack by calling the stream() member -// function of LogMessage which seems to avoid the problem. -#define LOG(severity) COMPACT_GOOGLE_LOG_ ## severity.stream() -#define SYSLOG(severity) SYSLOG_ ## severity(0).stream() - -namespace google { - -// They need the definitions of integer types. -#include "glog/log_severity.h" -#include "glog/vlog_is_on.h" - -// Initialize google's logging library. You will see the program name -// specified by argv0 in log outputs. -GOOGLE_GLOG_DLL_DECL void InitGoogleLogging(const char* argv0); - -// Install a function which will be called after LOG(FATAL). -GOOGLE_GLOG_DLL_DECL void InstallFailureFunction(void (*fail_func)()); - -class LogSink; // defined below - -// If a non-NULL sink pointer is given, we push this message to that sink. -// For LOG_TO_SINK we then do normal LOG(severity) logging as well. -// This is useful for capturing messages and passing/storing them -// somewhere more specific than the global log of the process. -// Argument types: -// LogSink* sink; -// LogSeverity severity; -// The cast is to disambiguate NULL arguments. -#define LOG_TO_SINK(sink, severity) \ - google::LogMessage( \ - __FILE__, __LINE__, \ - google::severity, \ - static_cast(sink), true).stream() -#define LOG_TO_SINK_BUT_NOT_TO_LOGFILE(sink, severity) \ - google::LogMessage( \ - __FILE__, __LINE__, \ - google::severity, \ - static_cast(sink), false).stream() - -// If a non-NULL string pointer is given, we write this message to that string. -// We then do normal LOG(severity) logging as well. -// This is useful for capturing messages and storing them somewhere more -// specific than the global log of the process. -// Argument types: -// string* message; -// LogSeverity severity; -// The cast is to disambiguate NULL arguments. -// NOTE: LOG(severity) expands to LogMessage().stream() for the specified -// severity. -#define LOG_TO_STRING(severity, message) \ - LOG_TO_STRING_##severity(static_cast(message)).stream() - -// If a non-NULL pointer is given, we push the message onto the end -// of a vector of strings; otherwise, we report it with LOG(severity). -// This is handy for capturing messages and perhaps passing them back -// to the caller, rather than reporting them immediately. -// Argument types: -// LogSeverity severity; -// vector *outvec; -// The cast is to disambiguate NULL arguments. -#define LOG_STRING(severity, outvec) \ - LOG_TO_STRING_##severity(static_cast*>(outvec)).stream() - -#define LOG_IF(severity, condition) \ - !(condition) ? (void) 0 : google::LogMessageVoidify() & LOG(severity) -#define SYSLOG_IF(severity, condition) \ - !(condition) ? (void) 0 : google::LogMessageVoidify() & SYSLOG(severity) - -#define LOG_ASSERT(condition) \ - LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition -#define SYSLOG_ASSERT(condition) \ - SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition - -// CHECK dies with a fatal error if condition is not true. It is *not* -// controlled by NDEBUG, so the check will be executed regardless of -// compilation mode. Therefore, it is safe to do things like: -// CHECK(fp->Write(x) == 4) -#define CHECK(condition) \ - LOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN(!(condition))) \ - << "Check failed: " #condition " " - -// A container for a string pointer which can be evaluated to a bool - -// true iff the pointer is NULL. -struct CheckOpString { - CheckOpString(std::string* str) : str_(str) { } - // No destructor: if str_ is non-NULL, we're about to LOG(FATAL), - // so there's no point in cleaning up str_. - operator bool() const { - return GOOGLE_PREDICT_BRANCH_NOT_TAKEN(str_ != NULL); - } - std::string* str_; -}; - -// Function is overloaded for integral types to allow static const -// integrals declared in classes and not defined to be used as arguments to -// CHECK* macros. It's not encouraged though. -template -inline const T& GetReferenceableValue(const T& t) { return t; } -inline char GetReferenceableValue(char t) { return t; } -inline unsigned char GetReferenceableValue(unsigned char t) { return t; } -inline signed char GetReferenceableValue(signed char t) { return t; } -inline short GetReferenceableValue(short t) { return t; } -inline unsigned short GetReferenceableValue(unsigned short t) { return t; } -inline int GetReferenceableValue(int t) { return t; } -inline unsigned int GetReferenceableValue(unsigned int t) { return t; } -inline long GetReferenceableValue(long t) { return t; } -inline unsigned long GetReferenceableValue(unsigned long t) { return t; } -inline long long GetReferenceableValue(long long t) { return t; } -inline unsigned long long GetReferenceableValue(unsigned long long t) { - return t; -} - -// This is a dummy class to define the following operator. -struct DummyClassToDefineOperator {}; - -} - -// Define global operator<< to declare using ::operator<<. -// This declaration will allow use to use CHECK macros for user -// defined classes which have operator<< (e.g., stl_logging.h). -inline std::ostream& operator<<( - std::ostream& out, const google::DummyClassToDefineOperator&) { - return out; -} - -namespace google { - -// Build the error message string. -template -std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) { - // It means that we cannot use stl_logging if compiler doesn't - // support using expression for operator. - // TODO(hamaji): Figure out a way to fix. -#if 1 - using ::operator<<; -#endif - std::strstream ss; - ss << names << " (" << v1 << " vs. " << v2 << ")"; - return new std::string(ss.str(), ss.pcount()); -} - -// Helper functions for CHECK_OP macro. -// The (int, int) specialization works around the issue that the compiler -// will not instantiate the template version of the function on values of -// unnamed enum type - see comment below. -#define DEFINE_CHECK_OP_IMPL(name, op) \ - template \ - inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \ - const char* names) { \ - if (v1 op v2) return NULL; \ - else return MakeCheckOpString(v1, v2, names); \ - } \ - inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \ - return Check##name##Impl(v1, v2, names); \ - } - -// Use _EQ, _NE, _LE, etc. in case the file including base/logging.h -// provides its own #defines for the simpler names EQ, NE, LE, etc. -// This happens if, for example, those are used as token names in a -// yacc grammar. -DEFINE_CHECK_OP_IMPL(_EQ, ==) -DEFINE_CHECK_OP_IMPL(_NE, !=) -DEFINE_CHECK_OP_IMPL(_LE, <=) -DEFINE_CHECK_OP_IMPL(_LT, < ) -DEFINE_CHECK_OP_IMPL(_GE, >=) -DEFINE_CHECK_OP_IMPL(_GT, > ) -#undef DEFINE_CHECK_OP_IMPL - -// Helper macro for binary operators. -// Don't use this macro directly in your code, use CHECK_EQ et al below. - -#if defined(STATIC_ANALYSIS) -// Only for static analysis tool to know that it is equivalent to assert -#define CHECK_OP_LOG(name, op, val1, val2, log) CHECK((val1) op (val2)) -#elif !defined(NDEBUG) -// In debug mode, avoid constructing CheckOpStrings if possible, -// to reduce the overhead of CHECK statments by 2x. -// Real DCHECK-heavy tests have seen 1.5x speedups. - -// The meaning of "string" might be different between now and -// when this macro gets invoked (e.g., if someone is experimenting -// with other string implementations that get defined after this -// file is included). Save the current meaning now and use it -// in the macro. -typedef std::string _Check_string; -#define CHECK_OP_LOG(name, op, val1, val2, log) \ - while (google::_Check_string* _result = \ - google::Check##name##Impl( \ - google::GetReferenceableValue(val1), \ - google::GetReferenceableValue(val2), \ - #val1 " " #op " " #val2)) \ - log(__FILE__, __LINE__, \ - google::CheckOpString(_result)).stream() -#else -// In optimized mode, use CheckOpString to hint to compiler that -// the while condition is unlikely. -#define CHECK_OP_LOG(name, op, val1, val2, log) \ - while (google::CheckOpString _result = \ - google::Check##name##Impl(GetReferenceableValue(val1), \ - GetReferenceableValue(val2), \ - #val1 " " #op " " #val2)) \ - log(__FILE__, __LINE__, _result).stream() -#endif // STATIC_ANALYSIS, !NDEBUG - -#if GOOGLE_STRIP_LOG <= 3 -#define CHECK_OP(name, op, val1, val2) \ - CHECK_OP_LOG(name, op, val1, val2, google::LogMessageFatal) -#else -#define CHECK_OP(name, op, val1, val2) \ - CHECK_OP_LOG(name, op, val1, val2, google::NullStreamFatal) -#endif // STRIP_LOG <= 3 - -// Equality/Inequality checks - compare two values, and log a FATAL message -// including the two values when the result is not as expected. The values -// must have operator<<(ostream, ...) defined. -// -// You may append to the error message like so: -// CHECK_NE(1, 2) << ": The world must be ending!"; -// -// We are very careful to ensure that each argument is evaluated exactly -// once, and that anything which is legal to pass as a function argument is -// legal here. In particular, the arguments may be temporary expressions -// which will end up being destroyed at the end of the apparent statement, -// for example: -// CHECK_EQ(string("abc")[1], 'b'); -// -// WARNING: These don't compile correctly if one of the arguments is a pointer -// and the other is NULL. To work around this, simply static_cast NULL to the -// type of the desired pointer. - -#define CHECK_EQ(val1, val2) CHECK_OP(_EQ, ==, val1, val2) -#define CHECK_NE(val1, val2) CHECK_OP(_NE, !=, val1, val2) -#define CHECK_LE(val1, val2) CHECK_OP(_LE, <=, val1, val2) -#define CHECK_LT(val1, val2) CHECK_OP(_LT, < , val1, val2) -#define CHECK_GE(val1, val2) CHECK_OP(_GE, >=, val1, val2) -#define CHECK_GT(val1, val2) CHECK_OP(_GT, > , val1, val2) - -// Check that the input is non NULL. This very useful in constructor -// initializer lists. - -#define CHECK_NOTNULL(val) \ - google::CheckNotNull(__FILE__, __LINE__, "'" #val "' Must be non NULL", (val)) - -// Helper functions for string comparisons. -// To avoid bloat, the definitions are in logging.cc. -#define DECLARE_CHECK_STROP_IMPL(func, expected) \ - GOOGLE_GLOG_DLL_DECL std::string* Check##func##expected##Impl( \ - const char* s1, const char* s2, const char* names); -DECLARE_CHECK_STROP_IMPL(strcmp, true) -DECLARE_CHECK_STROP_IMPL(strcmp, false) -DECLARE_CHECK_STROP_IMPL(strcasecmp, true) -DECLARE_CHECK_STROP_IMPL(strcasecmp, false) -#undef DECLARE_CHECK_STROP_IMPL - -// Helper macro for string comparisons. -// Don't use this macro directly in your code, use CHECK_STREQ et al below. -#define CHECK_STROP(func, op, expected, s1, s2) \ - while (google::CheckOpString _result = \ - google::Check##func##expected##Impl((s1), (s2), \ - #s1 " " #op " " #s2)) \ - LOG(FATAL) << *_result.str_ - - -// String (char*) equality/inequality checks. -// CASE versions are case-insensitive. -// -// Note that "s1" and "s2" may be temporary strings which are destroyed -// by the compiler at the end of the current "full expression" -// (e.g. CHECK_STREQ(Foo().c_str(), Bar().c_str())). - -#define CHECK_STREQ(s1, s2) CHECK_STROP(strcmp, ==, true, s1, s2) -#define CHECK_STRNE(s1, s2) CHECK_STROP(strcmp, !=, false, s1, s2) -#define CHECK_STRCASEEQ(s1, s2) CHECK_STROP(strcasecmp, ==, true, s1, s2) -#define CHECK_STRCASENE(s1, s2) CHECK_STROP(strcasecmp, !=, false, s1, s2) - -#define CHECK_INDEX(I,A) CHECK(I < (sizeof(A)/sizeof(A[0]))) -#define CHECK_BOUND(B,A) CHECK(B <= (sizeof(A)/sizeof(A[0]))) - -#define CHECK_DOUBLE_EQ(val1, val2) \ - do { \ - CHECK_LE((val1), (val2)+0.000000000000001L); \ - CHECK_GE((val1), (val2)-0.000000000000001L); \ - } while (0) - -#define CHECK_NEAR(val1, val2, margin) \ - do { \ - CHECK_LE((val1), (val2)+(margin)); \ - CHECK_GE((val1), (val2)-(margin)); \ - } while (0) - -// perror()..googly style! -// -// PLOG() and PLOG_IF() and PCHECK() behave exactly like their LOG* and -// CHECK equivalents with the addition that they postpend a description -// of the current state of errno to their output lines. - -#define PLOG(severity) GOOGLE_PLOG(severity, 0).stream() - -#define GOOGLE_PLOG(severity, counter) \ - google::ErrnoLogMessage( \ - __FILE__, __LINE__, google::severity, counter, \ - &google::LogMessage::SendToLog) - -#define PLOG_IF(severity, condition) \ - !(condition) ? (void) 0 : google::LogMessageVoidify() & PLOG(severity) - -// A CHECK() macro that postpends errno if the condition is false. E.g. -// -// if (poll(fds, nfds, timeout) == -1) { PCHECK(errno == EINTR); ... } -#define PCHECK(condition) \ - PLOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN(!(condition))) \ - << "Check failed: " #condition " " - -// A CHECK() macro that lets you assert the success of a function that -// returns -1 and sets errno in case of an error. E.g. -// -// CHECK_ERR(mkdir(path, 0700)); -// -// or -// -// int fd = open(filename, flags); CHECK_ERR(fd) << ": open " << filename; -#define CHECK_ERR(invocation) \ -PLOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN((invocation) == -1)) \ - << #invocation - -// Use macro expansion to create, for each use of LOG_EVERY_N(), static -// variables with the __LINE__ expansion as part of the variable name. -#define LOG_EVERY_N_VARNAME(base, line) LOG_EVERY_N_VARNAME_CONCAT(base, line) -#define LOG_EVERY_N_VARNAME_CONCAT(base, line) base ## line - -#define LOG_OCCURRENCES LOG_EVERY_N_VARNAME(occurrences_, __LINE__) -#define LOG_OCCURRENCES_MOD_N LOG_EVERY_N_VARNAME(occurrences_mod_n_, __LINE__) - -#define SOME_KIND_OF_LOG_EVERY_N(severity, n, what_to_do) \ - static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \ - ++LOG_OCCURRENCES; \ - if (++LOG_OCCURRENCES_MOD_N > n) LOG_OCCURRENCES_MOD_N -= n; \ - if (LOG_OCCURRENCES_MOD_N == 1) \ - google::LogMessage( \ - __FILE__, __LINE__, google::severity, LOG_OCCURRENCES, \ - &what_to_do).stream() - -#define SOME_KIND_OF_LOG_IF_EVERY_N(severity, condition, n, what_to_do) \ - static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \ - ++LOG_OCCURRENCES; \ - if (condition && \ - ((LOG_OCCURRENCES_MOD_N=(LOG_OCCURRENCES_MOD_N + 1) % n) == (1 % n))) \ - google::LogMessage( \ - __FILE__, __LINE__, google::severity, LOG_OCCURRENCES, \ - &what_to_do).stream() - -#define SOME_KIND_OF_PLOG_EVERY_N(severity, n, what_to_do) \ - static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \ - ++LOG_OCCURRENCES; \ - if (++LOG_OCCURRENCES_MOD_N > n) LOG_OCCURRENCES_MOD_N -= n; \ - if (LOG_OCCURRENCES_MOD_N == 1) \ - google::ErrnoLogMessage( \ - __FILE__, __LINE__, google::severity, LOG_OCCURRENCES, \ - &what_to_do).stream() - -#define SOME_KIND_OF_LOG_FIRST_N(severity, n, what_to_do) \ - static int LOG_OCCURRENCES = 0; \ - if (LOG_OCCURRENCES <= n) \ - ++LOG_OCCURRENCES; \ - if (LOG_OCCURRENCES <= n) \ - google::LogMessage( \ - __FILE__, __LINE__, google::severity, LOG_OCCURRENCES, \ - &what_to_do).stream() - -namespace glog_internal_namespace_ { -template -struct CompileAssert { -}; -struct CrashReason; -} // namespace glog_internal_namespace_ - -#define GOOGLE_GLOG_COMPILE_ASSERT(expr, msg) \ - typedef google::glog_internal_namespace_::CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] - -#define LOG_EVERY_N(severity, n) \ - GOOGLE_GLOG_COMPILE_ASSERT(google::severity < \ - google::NUM_SEVERITIES, \ - INVALID_REQUESTED_LOG_SEVERITY); \ - SOME_KIND_OF_LOG_EVERY_N(severity, (n), google::LogMessage::SendToLog) - -#define SYSLOG_EVERY_N(severity, n) \ - SOME_KIND_OF_LOG_EVERY_N(severity, (n), google::LogMessage::SendToSyslogAndLog) - -#define PLOG_EVERY_N(severity, n) \ - SOME_KIND_OF_PLOG_EVERY_N(severity, (n), google::LogMessage::SendToLog) - -#define LOG_FIRST_N(severity, n) \ - SOME_KIND_OF_LOG_FIRST_N(severity, (n), google::LogMessage::SendToLog) - -#define LOG_IF_EVERY_N(severity, condition, n) \ - SOME_KIND_OF_LOG_IF_EVERY_N(severity, (condition), (n), google::LogMessage::SendToLog) - -// We want the special COUNTER value available for LOG_EVERY_X()'ed messages -enum PRIVATE_Counter {COUNTER}; - - -// Plus some debug-logging macros that get compiled to nothing for production - -#ifndef NDEBUG - -#define DLOG(severity) LOG(severity) -#define DVLOG(verboselevel) VLOG(verboselevel) -#define DLOG_IF(severity, condition) LOG_IF(severity, condition) -#define DLOG_EVERY_N(severity, n) LOG_EVERY_N(severity, n) -#define DLOG_IF_EVERY_N(severity, condition, n) \ - LOG_IF_EVERY_N(severity, condition, n) -#define DLOG_ASSERT(condition) LOG_ASSERT(condition) - -// debug-only checking. not executed in NDEBUG mode. -#define DCHECK(condition) CHECK(condition) -#define DCHECK_EQ(val1, val2) CHECK_EQ(val1, val2) -#define DCHECK_NE(val1, val2) CHECK_NE(val1, val2) -#define DCHECK_LE(val1, val2) CHECK_LE(val1, val2) -#define DCHECK_LT(val1, val2) CHECK_LT(val1, val2) -#define DCHECK_GE(val1, val2) CHECK_GE(val1, val2) -#define DCHECK_GT(val1, val2) CHECK_GT(val1, val2) -#define DCHECK_STREQ(str1, str2) CHECK_STREQ(str1, str2) -#define DCHECK_STRCASEEQ(str1, str2) CHECK_STRCASEEQ(str1, str2) -#define DCHECK_STRNE(str1, str2) CHECK_STRNE(str1, str2) -#define DCHECK_STRCASENE(str1, str2) CHECK_STRCASENE(str1, str2) - -#else // NDEBUG - -#define DLOG(severity) \ - true ? (void) 0 : google::LogMessageVoidify() & LOG(severity) - -#define DVLOG(verboselevel) \ - (true || !VLOG_IS_ON(verboselevel)) ?\ - (void) 0 : google::LogMessageVoidify() & LOG(INFO) - -#define DLOG_IF(severity, condition) \ - (true || !(condition)) ? (void) 0 : google::LogMessageVoidify() & LOG(severity) - -#define DLOG_EVERY_N(severity, n) \ - true ? (void) 0 : google::LogMessageVoidify() & LOG(severity) - -#define DLOG_IF_EVERY_N(severity, condition, n) \ - (true || !(condition))? (void) 0 : google::LogMessageVoidify() & LOG(severity) - -#define DLOG_ASSERT(condition) \ - true ? (void) 0 : LOG_ASSERT(condition) - -#define DCHECK(condition) \ - while (false) \ - CHECK(condition) - -#define DCHECK_EQ(val1, val2) \ - while (false) \ - CHECK_EQ(val1, val2) - -#define DCHECK_NE(val1, val2) \ - while (false) \ - CHECK_NE(val1, val2) - -#define DCHECK_LE(val1, val2) \ - while (false) \ - CHECK_LE(val1, val2) - -#define DCHECK_LT(val1, val2) \ - while (false) \ - CHECK_LT(val1, val2) - -#define DCHECK_GE(val1, val2) \ - while (false) \ - CHECK_GE(val1, val2) - -#define DCHECK_GT(val1, val2) \ - while (false) \ - CHECK_GT(val1, val2) - -#define DCHECK_STREQ(str1, str2) \ - while (false) \ - CHECK_STREQ(str1, str2) - -#define DCHECK_STRCASEEQ(str1, str2) \ - while (false) \ - CHECK_STRCASEEQ(str1, str2) - -#define DCHECK_STRNE(str1, str2) \ - while (false) \ - CHECK_STRNE(str1, str2) - -#define DCHECK_STRCASENE(str1, str2) \ - while (false) \ - CHECK_STRCASENE(str1, str2) - - -#endif // NDEBUG - -// Log only in verbose mode. - -#define VLOG(verboselevel) LOG_IF(INFO, VLOG_IS_ON(verboselevel)) - -#define VLOG_IF(verboselevel, condition) \ - LOG_IF(INFO, (condition) && VLOG_IS_ON(verboselevel)) - -#define VLOG_EVERY_N(verboselevel, n) \ - LOG_IF_EVERY_N(INFO, VLOG_IS_ON(verboselevel), n) - -#define VLOG_IF_EVERY_N(verboselevel, condition, n) \ - LOG_IF_EVERY_N(INFO, (condition) && VLOG_IS_ON(verboselevel), n) - -// -// This class more or less represents a particular log message. You -// create an instance of LogMessage and then stream stuff to it. -// When you finish streaming to it, ~LogMessage is called and the -// full message gets streamed to the appropriate destination. -// -// You shouldn't actually use LogMessage's constructor to log things, -// though. You should use the LOG() macro (and variants thereof) -// above. -class GOOGLE_GLOG_DLL_DECL LogMessage { -public: - enum { - // Passing kNoLogPrefix for the line number disables the - // log-message prefix. Useful for using the LogMessage - // infrastructure as a printing utility. See also the --log_prefix - // flag for controlling the log-message prefix on an - // application-wide basis. - kNoLogPrefix = -1 - }; - - // LogStream inherit from non-DLL-exported class (std::ostrstream) - // and VC++ produces a warning for this situation. - // However, MSDN says "C4275 can be ignored in Microsoft Visual C++ - // 2005 if you are deriving from a type in the Standard C++ Library" - // http://msdn.microsoft.com/en-us/library/3tdb471s(VS.80).aspx - // Let's just ignore the warning. -#ifdef _MSC_VER -# pragma warning(disable: 4275) -#endif - class GOOGLE_GLOG_DLL_DECL LogStream : public std::ostrstream { -#ifdef _MSC_VER -# pragma warning(default: 4275) -#endif - public: - LogStream(char *buf, int len, int ctr) - : ostrstream(buf, len), - ctr_(ctr) { - self_ = this; - } - - int ctr() const { return ctr_; } - void set_ctr(int ctr) { ctr_ = ctr; } - LogStream* self() const { return self_; } - - private: - int ctr_; // Counter hack (for the LOG_EVERY_X() macro) - LogStream *self_; // Consistency check hack - }; - -public: - // icc 8 requires this typedef to avoid an internal compiler error. - typedef void (LogMessage::*SendMethod)(); - - LogMessage(const char* file, int line, LogSeverity severity, int ctr, - SendMethod send_method); - - // Two special constructors that generate reduced amounts of code at - // LOG call sites for common cases. - - // Used for LOG(INFO): Implied are: - // severity = INFO, ctr = 0, send_method = &LogMessage::SendToLog. - // - // Using this constructor instead of the more complex constructor above - // saves 19 bytes per call site. - LogMessage(const char* file, int line); - - // Used for LOG(severity) where severity != INFO. Implied - // are: ctr = 0, send_method = &LogMessage::SendToLog - // - // Using this constructor instead of the more complex constructor above - // saves 17 bytes per call site. - LogMessage(const char* file, int line, LogSeverity severity); - - // Constructor to log this message to a specified sink (if not NULL). - // Implied are: ctr = 0, send_method = &LogMessage::SendToSinkAndLog if - // also_send_to_log is true, send_method = &LogMessage::SendToSink otherwise. - LogMessage(const char* file, int line, LogSeverity severity, LogSink* sink, - bool also_send_to_log); - - // Constructor where we also give a vector pointer - // for storing the messages (if the pointer is not NULL). - // Implied are: ctr = 0, send_method = &LogMessage::SaveOrSendToLog. - LogMessage(const char* file, int line, LogSeverity severity, - std::vector* outvec); - - // Constructor where we also give a string pointer for storing the - // message (if the pointer is not NULL). Implied are: ctr = 0, - // send_method = &LogMessage::WriteToStringAndLog. - LogMessage(const char* file, int line, LogSeverity severity, - std::string* message); - - // A special constructor used for check failures - LogMessage(const char* file, int line, const CheckOpString& result); - - ~LogMessage(); - - // Flush a buffered message to the sink set in the constructor. Always - // called by the destructor, it may also be called from elsewhere if - // needed. Only the first call is actioned; any later ones are ignored. - void Flush(); - - // An arbitrary limit on the length of a single log message. This - // is so that streaming can be done more efficiently. - static const size_t kMaxLogMessageLen; - - // Theses should not be called directly outside of logging.*, - // only passed as SendMethod arguments to other LogMessage methods: - void SendToLog(); // Actually dispatch to the logs - void SendToSyslogAndLog(); // Actually dispatch to syslog and the logs - - // Call abort() or similar to perform LOG(FATAL) crash. - static void Fail() __attribute__ ((noreturn)); - - std::ostream& stream() { return *(data_->stream_); } - - int preserved_errno() const { return data_->preserved_errno_; } - - // Must be called without the log_mutex held. (L < log_mutex) - static int64 num_messages(int severity); - -private: - // Fully internal SendMethod cases: - void SendToSinkAndLog(); // Send to sink if provided and dispatch to the logs - void SendToSink(); // Send to sink if provided, do nothing otherwise. - - // Write to string if provided and dispatch to the logs. - void WriteToStringAndLog(); - - void SaveOrSendToLog(); // Save to stringvec if provided, else to logs - - void Init(const char* file, int line, LogSeverity severity, - void (LogMessage::*send_method)()); - - // Used to fill in crash information during LOG(FATAL) failures. - void RecordCrashReason(glog_internal_namespace_::CrashReason* reason); - - // Counts of messages sent at each priority: - static int64 num_messages_[NUM_SEVERITIES]; // under log_mutex - - // We keep the data in a separate struct so that each instance of - // LogMessage uses less stack space. - struct GOOGLE_GLOG_DLL_DECL LogMessageData { - LogMessageData() {}; - - int preserved_errno_; // preserved errno - char* buf_; - char* message_text_; // Complete message text (points to selected buffer) - LogStream* stream_alloc_; - LogStream* stream_; - char severity_; // What level is this LogMessage logged at? - int line_; // line number where logging call is. - void (LogMessage::*send_method_)(); // Call this in destructor to send - union { // At most one of these is used: union to keep the size low. - LogSink* sink_; // NULL or sink to send message to - std::vector* outvec_; // NULL or vector to push message onto - std::string* message_; // NULL or string to write message into - }; - time_t timestamp_; // Time of creation of LogMessage - struct ::tm tm_time_; // Time of creation of LogMessage - size_t num_prefix_chars_; // # of chars of prefix in this message - size_t num_chars_to_log_; // # of chars of msg to send to log - size_t num_chars_to_syslog_; // # of chars of msg to send to syslog - const char* basename_; // basename of file that called LOG - const char* fullname_; // fullname of file that called LOG - bool has_been_flushed_; // false => data has not been flushed - bool first_fatal_; // true => this was first fatal msg - - ~LogMessageData(); - private: - LogMessageData(const LogMessageData&); - void operator=(const LogMessageData&); - }; - - static LogMessageData fatal_msg_data_exclusive_; - static LogMessageData fatal_msg_data_shared_; - - LogMessageData* allocated_; - LogMessageData* data_; - - friend class LogDestination; - - LogMessage(const LogMessage&); - void operator=(const LogMessage&); -}; - -// This class happens to be thread-hostile because all instances share -// a single data buffer, but since it can only be created just before -// the process dies, we don't worry so much. -class GOOGLE_GLOG_DLL_DECL LogMessageFatal : public LogMessage { - public: - LogMessageFatal(const char* file, int line); - LogMessageFatal(const char* file, int line, const CheckOpString& result); - ~LogMessageFatal() __attribute__ ((noreturn)); -}; - -// A non-macro interface to the log facility; (useful -// when the logging level is not a compile-time constant). -inline void LogAtLevel(int const severity, std::string const &msg) { - LogMessage(__FILE__, __LINE__, severity).stream() << msg; -} - -// A macro alternative of LogAtLevel. New code may want to use this -// version since there are two advantages: 1. this version outputs the -// file name and the line number where this macro is put like other -// LOG macros, 2. this macro can be used as C++ stream. -#define LOG_AT_LEVEL(severity) LogMessage(__FILE__, __LINE__, severity).stream() - -// A small helper for CHECK_NOTNULL(). -template -T* CheckNotNull(const char *file, int line, const char *names, T* t) { - if (t == NULL) { - LogMessageFatal(file, line, new std::string(names)); - } - return t; -} - -// Allow folks to put a counter in the LOG_EVERY_X()'ed messages. This -// only works if ostream is a LogStream. If the ostream is not a -// LogStream you'll get an assert saying as much at runtime. -GOOGLE_GLOG_DLL_DECL std::ostream& operator<<(std::ostream &os, - const PRIVATE_Counter&); - - -// Derived class for PLOG*() above. -class GOOGLE_GLOG_DLL_DECL ErrnoLogMessage : public LogMessage { - public: - - ErrnoLogMessage(const char* file, int line, LogSeverity severity, int ctr, - void (LogMessage::*send_method)()); - - // Postpends ": strerror(errno) [errno]". - ~ErrnoLogMessage(); - - private: - ErrnoLogMessage(const ErrnoLogMessage&); - void operator=(const ErrnoLogMessage&); -}; - - -// This class is used to explicitly ignore values in the conditional -// logging macros. This avoids compiler warnings like "value computed -// is not used" and "statement has no effect". - -class GOOGLE_GLOG_DLL_DECL LogMessageVoidify { - public: - LogMessageVoidify() { } - // This has to be an operator with a precedence lower than << but - // higher than ?: - void operator&(std::ostream&) { } -}; - - -// Flushes all log files that contains messages that are at least of -// the specified severity level. Thread-safe. -GOOGLE_GLOG_DLL_DECL void FlushLogFiles(LogSeverity min_severity); - -// Flushes all log files that contains messages that are at least of -// the specified severity level. Thread-hostile because it ignores -// locking -- used for catastrophic failures. -GOOGLE_GLOG_DLL_DECL void FlushLogFilesUnsafe(LogSeverity min_severity); - -// -// Set the destination to which a particular severity level of log -// messages is sent. If base_filename is "", it means "don't log this -// severity". Thread-safe. -// -GOOGLE_GLOG_DLL_DECL void SetLogDestination(LogSeverity severity, - const char* base_filename); - -// -// Set the basename of the symlink to the latest log file at a given -// severity. If symlink_basename is empty, do not make a symlink. If -// you don't call this function, the symlink basename is the -// invocation name of the program. Thread-safe. -// -GOOGLE_GLOG_DLL_DECL void SetLogSymlink(LogSeverity severity, - const char* symlink_basename); - -// -// Used to send logs to some other kind of destination -// Users should subclass LogSink and override send to do whatever they want. -// Implementations must be thread-safe because a shared instance will -// be called from whichever thread ran the LOG(XXX) line. -class GOOGLE_GLOG_DLL_DECL LogSink { - public: - virtual ~LogSink(); - - // Sink's logging logic (message_len is such as to exclude '\n' at the end). - // This method can't use LOG() or CHECK() as logging system mutex(s) are held - // during this call. - virtual void send(LogSeverity severity, const char* full_filename, - const char* base_filename, int line, - const struct ::tm* tm_time, - const char* message, size_t message_len) = 0; - - // Redefine this to implement waiting for - // the sink's logging logic to complete. - // It will be called after each send() returns, - // but before that LogMessage exits or crashes. - // By default this function does nothing. - // Using this function one can implement complex logic for send() - // that itself involves logging; and do all this w/o causing deadlocks and - // inconsistent rearrangement of log messages. - // E.g. if a LogSink has thread-specific actions, the send() method - // can simply add the message to a queue and wake up another thread that - // handles real logging while itself making some LOG() calls; - // WaitTillSent() can be implemented to wait for that logic to complete. - // See our unittest for an example. - virtual void WaitTillSent(); - - // Returns the normal text output of the log message. - // Can be useful to implement send(). - static std::string ToString(LogSeverity severity, const char* file, int line, - const struct ::tm* tm_time, - const char* message, size_t message_len); -}; - -// Add or remove a LogSink as a consumer of logging data. Thread-safe. -GOOGLE_GLOG_DLL_DECL void AddLogSink(LogSink *destination); -GOOGLE_GLOG_DLL_DECL void RemoveLogSink(LogSink *destination); - -// -// Specify an "extension" added to the filename specified via -// SetLogDestination. This applies to all severity levels. It's -// often used to append the port we're listening on to the logfile -// name. Thread-safe. -// -GOOGLE_GLOG_DLL_DECL void SetLogFilenameExtension( - const char* filename_extension); - -// -// Make it so that all log messages of at least a particular severity -// are logged to stderr (in addition to logging to the usual log -// file(s)). Thread-safe. -// -GOOGLE_GLOG_DLL_DECL void SetStderrLogging(LogSeverity min_severity); - -// -// Make it so that all log messages go only to stderr. Thread-safe. -// -GOOGLE_GLOG_DLL_DECL void LogToStderr(); - -// -// Make it so that all log messages of at least a particular severity are -// logged via email to a list of addresses (in addition to logging to the -// usual log file(s)). The list of addresses is just a string containing -// the email addresses to send to (separated by spaces, say). Thread-safe. -// -GOOGLE_GLOG_DLL_DECL void SetEmailLogging(LogSeverity min_severity, - const char* addresses); - -// A simple function that sends email. dest is a commma-separated -// list of addressess. Thread-safe. -GOOGLE_GLOG_DLL_DECL bool SendEmail(const char *dest, - const char *subject, const char *body); - -GOOGLE_GLOG_DLL_DECL const std::vector& GetLoggingDirectories(); - -// For tests only: Clear the internal [cached] list of logging directories to -// force a refresh the next time GetLoggingDirectories is called. -// Thread-hostile. -void TestOnly_ClearLoggingDirectoriesList(); - -// Returns a set of existing temporary directories, which will be a -// subset of the directories returned by GetLogginDirectories(). -// Thread-safe. -GOOGLE_GLOG_DLL_DECL void GetExistingTempDirectories( - std::vector* list); - -// Print any fatal message again -- useful to call from signal handler -// so that the last thing in the output is the fatal message. -// Thread-hostile, but a race is unlikely. -GOOGLE_GLOG_DLL_DECL void ReprintFatalMessage(); - -// Truncate a log file that may be the append-only output of multiple -// processes and hence can't simply be renamed/reopened (typically a -// stdout/stderr). If the file "path" is > "limit" bytes, copy the -// last "keep" bytes to offset 0 and truncate the rest. Since we could -// be racing with other writers, this approach has the potential to -// lose very small amounts of data. For security, only follow symlinks -// if the path is /proc/self/fd/* -GOOGLE_GLOG_DLL_DECL void TruncateLogFile(const char *path, - int64 limit, int64 keep); - -// Truncate stdout and stderr if they are over the value specified by -// --max_log_size; keep the final 1MB. This function has the same -// race condition as TruncateLogFile. -GOOGLE_GLOG_DLL_DECL void TruncateStdoutStderr(); - -// Return the string representation of the provided LogSeverity level. -// Thread-safe. -GOOGLE_GLOG_DLL_DECL const char* GetLogSeverityName(LogSeverity severity); - -// --------------------------------------------------------------------- -// Implementation details that are not useful to most clients -// --------------------------------------------------------------------- - -// A Logger is the interface used by logging modules to emit entries -// to a log. A typical implementation will dump formatted data to a -// sequence of files. We also provide interfaces that will forward -// the data to another thread so that the invoker never blocks. -// Implementations should be thread-safe since the logging system -// will write to them from multiple threads. - -namespace base { - -class GOOGLE_GLOG_DLL_DECL Logger { - public: - virtual ~Logger(); - - // Writes "message[0,message_len-1]" corresponding to an event that - // occurred at "timestamp". If "force_flush" is true, the log file - // is flushed immediately. - // - // The input message has already been formatted as deemed - // appropriate by the higher level logging facility. For example, - // textual log messages already contain timestamps, and the - // file:linenumber header. - virtual void Write(bool force_flush, - time_t timestamp, - const char* message, - int message_len) = 0; - - // Flush any buffered messages - virtual void Flush() = 0; - - // Get the current LOG file size. - // The returned value is approximate since some - // logged data may not have been flushed to disk yet. - virtual uint32 LogSize() = 0; -}; - -// Get the logger for the specified severity level. The logger -// remains the property of the logging module and should not be -// deleted by the caller. Thread-safe. -extern GOOGLE_GLOG_DLL_DECL Logger* GetLogger(LogSeverity level); - -// Set the logger for the specified severity level. The logger -// becomes the property of the logging module and should not -// be deleted by the caller. Thread-safe. -extern GOOGLE_GLOG_DLL_DECL void SetLogger(LogSeverity level, Logger* logger); - -} - -// glibc has traditionally implemented two incompatible versions of -// strerror_r(). There is a poorly defined convention for picking the -// version that we want, but it is not clear whether it even works with -// all versions of glibc. -// So, instead, we provide this wrapper that automatically detects the -// version that is in use, and then implements POSIX semantics. -// N.B. In addition to what POSIX says, we also guarantee that "buf" will -// be set to an empty string, if this function failed. This means, in most -// cases, you do not need to check the error code and you can directly -// use the value of "buf". It will never have an undefined value. -GOOGLE_GLOG_DLL_DECL int posix_strerror_r(int err, char *buf, size_t len); - - -// A class for which we define operator<<, which does nothing. -class GOOGLE_GLOG_DLL_DECL NullStream : public LogMessage::LogStream { - public: - // Initialize the LogStream so the messages can be written somewhere - // (they'll never be actually displayed). This will be needed if a - // NullStream& is implicitly converted to LogStream&, in which case - // the overloaded NullStream::operator<< will not be invoked. - NullStream() : LogMessage::LogStream(message_buffer_, 1, 0) { } - NullStream(const char* /*file*/, int /*line*/, - const CheckOpString& /*result*/) : - LogMessage::LogStream(message_buffer_, 1, 0) { } - NullStream &stream() { return *this; } - private: - // A very short buffer for messages (which we discard anyway). This - // will be needed if NullStream& converted to LogStream& (e.g. as a - // result of a conditional expression). - char message_buffer_[2]; -}; - -// Do nothing. This operator is inline, allowing the message to be -// compiled away. The message will not be compiled away if we do -// something like (flag ? LOG(INFO) : LOG(ERROR)) << message; when -// SKIP_LOG=WARNING. In those cases, NullStream will be implicitly -// converted to LogStream and the message will be computed and then -// quietly discarded. -template -inline NullStream& operator<<(NullStream &str, const T &value) { return str; } - -// Similar to NullStream, but aborts the program (without stack -// trace), like LogMessageFatal. -class GOOGLE_GLOG_DLL_DECL NullStreamFatal : public NullStream { - public: - NullStreamFatal() { } - NullStreamFatal(const char* file, int line, const CheckOpString& result) : - NullStream(file, line, result) { } - __attribute__ ((noreturn)) ~NullStreamFatal() { _exit(1); } -}; - -// Install a signal handler that will dump signal information and a stack -// trace when the program crashes on certain signals. We'll install the -// signal handler for the following signals. -// -// SIGSEGV, SIGILL, SIGFPE, SIGABRT, SIGBUS, and SIGTERM. -// -// By default, the signal handler will write the failure dump to the -// standard error. You can customize the destination by installing your -// own writer function by InstallFailureWriter() below. -// -// Note on threading: -// -// The function should be called before threads are created, if you want -// to use the failure signal handler for all threads. The stack trace -// will be shown only for the thread that receives the signal. In other -// words, stack traces of other threads won't be shown. -GOOGLE_GLOG_DLL_DECL void InstallFailureSignalHandler(); - -// Installs a function that is used for writing the failure dump. "data" -// is the pointer to the beginning of a message to be written, and "size" -// is the size of the message. You should not expect the data is -// terminated with '\0'. -GOOGLE_GLOG_DLL_DECL void InstallFailureWriter( - void (*writer)(const char* data, int size)); - -} - -#endif // _LOGGING_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/raw_logging.h b/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/raw_logging.h deleted file mode 100644 index 9e9b3772f3b..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/raw_logging.h +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright (c) 2006, 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. -// -// Author: Maxim Lifantsev -// -// Thread-safe logging routines that do not allocate any memory or -// acquire any locks, and can therefore be used by low-level memory -// allocation and synchronization code. - -#ifndef BASE_RAW_LOGGING_H_ -#define BASE_RAW_LOGGING_H_ - -#include - -namespace google { - -#include "glog/log_severity.h" -#include "glog/vlog_is_on.h" - -// Annoying stuff for windows -- makes sure clients can import these functions -#ifndef GOOGLE_GLOG_DLL_DECL -# if defined(_WIN32) && !defined(__CYGWIN__) -# define GOOGLE_GLOG_DLL_DECL __declspec(dllimport) -# else -# define GOOGLE_GLOG_DLL_DECL -# endif -#endif - -// This is similar to LOG(severity) << format... and VLOG(level) << format.., -// but -// * it is to be used ONLY by low-level modules that can't use normal LOG() -// * it is desiged to be a low-level logger that does not allocate any -// memory and does not need any locks, hence: -// * it logs straight and ONLY to STDERR w/o buffering -// * it uses an explicit format and arguments list -// * it will silently chop off really long message strings -// Usage example: -// RAW_LOG(ERROR, "Failed foo with %i: %s", status, error); -// RAW_VLOG(3, "status is %i", status); -// These will print an almost standard log lines like this to stderr only: -// E0821 211317 file.cc:123] RAW: Failed foo with 22: bad_file -// I0821 211317 file.cc:142] RAW: status is 20 -#define RAW_LOG(severity, ...) \ - do { \ - switch (google::severity) { \ - case 0: \ - RAW_LOG_INFO(__VA_ARGS__); \ - break; \ - case 1: \ - RAW_LOG_WARNING(__VA_ARGS__); \ - break; \ - case 2: \ - RAW_LOG_ERROR(__VA_ARGS__); \ - break; \ - case 3: \ - RAW_LOG_FATAL(__VA_ARGS__); \ - break; \ - default: \ - break; \ - } \ - } while (0) - -// The following STRIP_LOG testing is performed in the header file so that it's -// possible to completely compile out the logging code and the log messages. -#if STRIP_LOG == 0 -#define RAW_VLOG(verboselevel, ...) \ - do { \ - if (VLOG_IS_ON(verboselevel)) { \ - RAW_LOG_INFO(__VA_ARGS__); \ - } \ - } while (0) -#else -#define RAW_VLOG(verboselevel, ...) RawLogStub__(0, __VA_ARGS__) -#endif // STRIP_LOG == 0 - -#if STRIP_LOG == 0 -#define RAW_LOG_INFO(...) google::RawLog__(google::INFO, \ - __FILE__, __LINE__, __VA_ARGS__) -#else -#define RAW_LOG_INFO(...) google::RawLogStub__(0, __VA_ARGS__) -#endif // STRIP_LOG == 0 - -#if STRIP_LOG <= 1 -#define RAW_LOG_WARNING(...) google::RawLog__(google::WARNING, \ - __FILE__, __LINE__, __VA_ARGS__) -#else -#define RAW_LOG_WARNING(...) google::RawLogStub__(0, __VA_ARGS__) -#endif // STRIP_LOG <= 1 - -#if STRIP_LOG <= 2 -#define RAW_LOG_ERROR(...) google::RawLog__(google::ERROR, \ - __FILE__, __LINE__, __VA_ARGS__) -#else -#define RAW_LOG_ERROR(...) google::RawLogStub__(0, __VA_ARGS__) -#endif // STRIP_LOG <= 2 - -#if STRIP_LOG <= 3 -#define RAW_LOG_FATAL(...) google::RawLog__(google::FATAL, \ - __FILE__, __LINE__, __VA_ARGS__) -#else -#define RAW_LOG_FATAL(...) \ - do { \ - google::RawLogStub__(0, __VA_ARGS__); \ - exit(1); \ - } while (0) -#endif // STRIP_LOG <= 3 - -// Similar to CHECK(condition) << message, -// but for low-level modules: we use only RAW_LOG that does not allocate memory. -// We do not want to provide args list here to encourage this usage: -// if (!cond) RAW_LOG(FATAL, "foo ...", hard_to_compute_args); -// so that the args are not computed when not needed. -#define RAW_CHECK(condition, message) \ - do { \ - if (!(condition)) { \ - RAW_LOG(FATAL, "Check %s failed: %s", #condition, message); \ - } \ - } while (0) - -// Debug versions of RAW_LOG and RAW_CHECK -#ifndef NDEBUG - -#define RAW_DLOG(severity, ...) RAW_LOG(severity, __VA_ARGS__) -#define RAW_DCHECK(condition, message) RAW_CHECK(condition, message) - -#else // NDEBUG - -#define RAW_DLOG(severity, ...) \ - while (false) \ - RAW_LOG(severity, __VA_ARGS__) -#define RAW_DCHECK(condition, message) \ - while (false) \ - RAW_CHECK(condition, message) - -#endif // NDEBUG - -// Stub log function used to work around for unused variable warnings when -// building with STRIP_LOG > 0. -static inline void RawLogStub__(int ignored, ...) { -} - -// Helper function to implement RAW_LOG and RAW_VLOG -// Logs format... at "severity" level, reporting it -// as called from file:line. -// This does not allocate memory or acquire locks. -GOOGLE_GLOG_DLL_DECL void RawLog__(LogSeverity severity, - const char* file, - int line, - const char* format, ...) - __attribute__((__format__ (__printf__, 4, 5))); - -// Hack to propagate time information into this module so that -// this module does not have to directly call localtime_r(), -// which could allocate memory. -GOOGLE_GLOG_DLL_DECL void RawLog__SetLastTime(const struct tm& t, int usecs); - -} - -#endif // BASE_RAW_LOGGING_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/stl_logging.h b/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/stl_logging.h deleted file mode 100644 index 42f256065f6..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/stl_logging.h +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (c) 2003, 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. -// -// Stream output operators for STL containers; to be used for logging *only*. -// Inclusion of this file lets you do: -// -// list x; -// LOG(INFO) << "data: " << x; -// vector v1, v2; -// CHECK_EQ(v1, v2); -// -// Note that if you want to use these operators from the non-global namespace, -// you may get an error since they are not in namespace std (and they are not -// in namespace std since that would result in undefined behavior). You may -// need to write -// -// using ::operator<<; -// -// to fix these errors. - -#ifndef UTIL_GTL_STL_LOGGING_INL_H_ -#define UTIL_GTL_STL_LOGGING_INL_H_ - -#if !1 -# error We do not support stl_logging for this compiler -#endif - -#include -#include -#include -#include -#include -#include -#include - -#ifdef __GNUC__ -# include -# include -# include -#endif - -template -inline std::ostream& operator<<(std::ostream& out, - const std::pair& p) { - out << '(' << p.first << ", " << p.second << ')'; - return out; -} - -namespace google { - -template -inline void PrintSequence(std::ostream& out, Iter begin, Iter end) { - using ::operator<<; - // Output at most 100 elements -- appropriate if used for logging. - for (int i = 0; begin != end && i < 100; ++i, ++begin) { - if (i > 0) out << ' '; - out << *begin; - } - if (begin != end) { - out << " ..."; - } -} - -} - -#define OUTPUT_TWO_ARG_CONTAINER(Sequence) \ -template \ -inline std::ostream& operator<<(std::ostream& out, \ - const Sequence& seq) { \ - google::PrintSequence(out, seq.begin(), seq.end()); \ - return out; \ -} - -OUTPUT_TWO_ARG_CONTAINER(std::vector) -OUTPUT_TWO_ARG_CONTAINER(std::deque) -OUTPUT_TWO_ARG_CONTAINER(std::list) -#ifdef __GNUC__ -OUTPUT_TWO_ARG_CONTAINER(__gnu_cxx::slist) -#endif - -#undef OUTPUT_TWO_ARG_CONTAINER - -#define OUTPUT_THREE_ARG_CONTAINER(Sequence) \ -template \ -inline std::ostream& operator<<(std::ostream& out, \ - const Sequence& seq) { \ - google::PrintSequence(out, seq.begin(), seq.end()); \ - return out; \ -} - -OUTPUT_THREE_ARG_CONTAINER(std::set) -OUTPUT_THREE_ARG_CONTAINER(std::multiset) - -#undef OUTPUT_THREE_ARG_CONTAINER - -#define OUTPUT_FOUR_ARG_CONTAINER(Sequence) \ -template \ -inline std::ostream& operator<<(std::ostream& out, \ - const Sequence& seq) { \ - google::PrintSequence(out, seq.begin(), seq.end()); \ - return out; \ -} - -OUTPUT_FOUR_ARG_CONTAINER(std::map) -OUTPUT_FOUR_ARG_CONTAINER(std::multimap) -#ifdef __GNUC__ -OUTPUT_FOUR_ARG_CONTAINER(__gnu_cxx::hash_set) -OUTPUT_FOUR_ARG_CONTAINER(__gnu_cxx::hash_multiset) -#endif - -#undef OUTPUT_FOUR_ARG_CONTAINER - -#define OUTPUT_FIVE_ARG_CONTAINER(Sequence) \ -template \ -inline std::ostream& operator<<(std::ostream& out, \ - const Sequence& seq) { \ - google::PrintSequence(out, seq.begin(), seq.end()); \ - return out; \ -} - -#ifdef __GNUC__ -OUTPUT_FIVE_ARG_CONTAINER(__gnu_cxx::hash_map) -OUTPUT_FIVE_ARG_CONTAINER(__gnu_cxx::hash_multimap) -#endif - -#undef OUTPUT_FIVE_ARG_CONTAINER - -#endif // UTIL_GTL_STL_LOGGING_INL_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/vlog_is_on.h b/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/vlog_is_on.h deleted file mode 100644 index 7d94efcef2d..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/vlog_is_on.h +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) 1999, 2007, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: Ray Sidney and many others -// -// Defines the VLOG_IS_ON macro that controls the variable-verbosity -// conditional logging. -// -// It's used by VLOG and VLOG_IF in logging.h -// and by RAW_VLOG in raw_logging.h to trigger the logging. -// -// It can also be used directly e.g. like this: -// if (VLOG_IS_ON(2)) { -// // do some logging preparation and logging -// // that can't be accomplished e.g. via just VLOG(2) << ...; -// } -// -// The truth value that VLOG_IS_ON(level) returns is determined by -// the three verbosity level flags: -// --v= Gives the default maximal active V-logging level; -// 0 is the default. -// Normally positive values are used for V-logging levels. -// --vmodule= Gives the per-module maximal V-logging levels to override -// the value given by --v. -// E.g. "my_module=2,foo*=3" would change the logging level -// for all code in source files "my_module.*" and "foo*.*" -// ("-inl" suffixes are also disregarded for this matching). -// -// SetVLOGLevel helper function is provided to do limited dynamic control over -// V-logging by overriding the per-module settings given via --vmodule flag. -// -// CAVEAT: --vmodule functionality is not available in non gcc compilers. -// - -#ifndef BASE_VLOG_IS_ON_H_ -#define BASE_VLOG_IS_ON_H_ - -#include "glog/log_severity.h" - -// Annoying stuff for windows -- makes sure clients can import these functions -#ifndef GOOGLE_GLOG_DLL_DECL -# if defined(_WIN32) && !defined(__CYGWIN__) -# define GOOGLE_GLOG_DLL_DECL __declspec(dllimport) -# else -# define GOOGLE_GLOG_DLL_DECL -# endif -#endif - -#if defined(__GNUC__) -// We emit an anonymous static int* variable at every VLOG_IS_ON(n) site. -// (Normally) the first time every VLOG_IS_ON(n) site is hit, -// we determine what variable will dynamically control logging at this site: -// it's either FLAGS_v or an appropriate internal variable -// matching the current source file that represents results of -// parsing of --vmodule flag and/or SetVLOGLevel calls. -#define VLOG_IS_ON(verboselevel) \ - ({ static google::int32* vlocal__ = &google::kLogSiteUninitialized; \ - google::int32 verbose_level__ = (verboselevel); \ - (*vlocal__ >= verbose_level__) && \ - ((vlocal__ != &google::kLogSiteUninitialized) || \ - (google::InitVLOG3__(&vlocal__, &FLAGS_v, \ - __FILE__, verbose_level__))); }) -#else -// GNU extensions not available, so we do not support --vmodule. -// Dynamic value of FLAGS_v always controls the logging level. -#define VLOG_IS_ON(verboselevel) (FLAGS_v >= (verboselevel)) -#endif - -// Set VLOG(_IS_ON) level for module_pattern to log_level. -// This lets us dynamically control what is normally set by the --vmodule flag. -// Returns the level that previously applied to module_pattern. -// NOTE: To change the log level for VLOG(_IS_ON) sites -// that have already executed after/during InitGoogleLogging, -// one needs to supply the exact --vmodule pattern that applied to them. -// (If no --vmodule pattern applied to them -// the value of FLAGS_v will continue to control them.) -extern GOOGLE_GLOG_DLL_DECL int SetVLOGLevel(const char* module_pattern, - int log_level); - -// Various declarations needed for VLOG_IS_ON above: ========================= - -// Special value used to indicate that a VLOG_IS_ON site has not been -// initialized. We make this a large value, so the common-case check -// of "*vlocal__ >= verbose_level__" in VLOG_IS_ON definition -// passes in such cases and InitVLOG3__ is then triggered. -extern google::int32 kLogSiteUninitialized; - -// Helper routine which determines the logging info for a particalur VLOG site. -// site_flag is the address of the site-local pointer to the controlling -// verbosity level -// site_default is the default to use for *site_flag -// fname is the current source file name -// verbose_level is the argument to VLOG_IS_ON -// We will return the return value for VLOG_IS_ON -// and if possible set *site_flag appropriately. -extern GOOGLE_GLOG_DLL_DECL bool InitVLOG3__( - google::int32** site_flag, - google::int32* site_default, - const char* fname, - google::int32 verbose_level); - -#endif // BASE_VLOG_IS_ON_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/third_party/linux/lib/gflags/libgflags.a b/toolkit/crashreporter/google-breakpad/src/third_party/linux/lib/gflags/libgflags.a deleted file mode 100644 index c0de874c969cb05c58fa24af1293f65642b26dc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 390804 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

!0=+9>P9;DOC)}##LqfH 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%0DhCzeZAii+#&B z<**#3x0TGd&h~UkeZA!YIeso5NpY{Om(;saQhuAfF~|Fk52zxh{1LKwjuRzEN>@vs zZ@Q#?b0qc5m(;Vuu9uW=l-$2rd*uFYg@tKbMI3DbHgyS0=PjUPQ#|s^=lT&hR zk+lD9Nqd`jK-xQAGA;{H^J1xe$LS|J|VK++JbsMU^cPXZQ4@t)P5lK7N*;gd>q+==`DcPq_wLg~3 zgUNDejv02g{i|JU*GlHgF3CL8F~*uVRg(Spbjf?u%jrWT>*FZL6XZ)NraHb)GC%$) zsqbm0Z;|9{b}UkVM@ha@B>8^i^j|u?*6E9#{+!ddOXl4Nl6iN?amc(oN%Fks%gre+ zmUSt9WoJt6S0_2TeVJrmT`#%+%TC|rxK$_8Z_d$0^8DQ;<<50HNb=n^%;`Uszh!@t zwcJN?Wc*6mkYclZF~!G{@ow2U%6PX!jknI`)3|n$x3Hf$?kgG3%jDD?qwNIA{J2>% zKW5qoB>A6^w7=eNlpL(SOYTXrN3KbczFcWX8>=(>G!M_P{cN=zV{bwA^PlCjDHh7- zQamjeq}V9wueT)gsKxP>xPzqMJ3BsC{w~Lrjwea>mETM9{YmnC58E~JZz*1s9HjED z+?1ku7o@+dB>jD|q`&*h%X3^J>6a@UUyte59Fr8YFW)N1 z2G#ywNa~-cblN{v(*Lt0{a+{P|7DVXZm?fCiJ1HA3`2RLt+BQC9=lPpj=gSmcA@&A zyB#N~=O+80T_PFRryOrajq5hWyw@Mf8H^YINzfAWqp9L!d*yR6@{&|ewU;H|V!Ohw zwomW1lzYbIu#~G$<#hYJJ_VnPAxrv(RQdaSV=AZFXJWR`$j_&8TD?42@e96=rT&*) zew*W^c87h{*LT|2?VJ1fmh-Q*@7nj=-kdA#Zkj9YZnjT+AnTUe*UBF1e1|zcobNDQ z=U>V9xqae;a^<;O@I9$~ZFz4b12agr%rQb9oMWP-9Wx~Noh!E|PKm{KIi8V}d&SpF zKar2=6Z!MGoW`#FCH+YLE>?P#J%oK&ar<0Oq0hVBPrFh%yD@j*J5tx*a60{3%6H`N zQ>7o}=VLy$Qhbb`^GmM(SoaIRf0XvHj`2AYxwD`Dc-DKxUHHD1t#GI^7Sw7 zy?*wm%Mb9lu}+kE@jdn)v?2ehY*H|;r7>9nWJKiV@;@nt!NOXBg4%X{#t@h$VII#>3W@ACbnd`I&g zAUoygC*SbCLwZ-mt-U^y-be8ezaMXVzp8OP@1!_O*MI2xm%F`O=kp_SBY%h6vhIA+ zt}8y9pU>mSAEh`=ewZrl`#4ql>A!y8NH6pD%6;bVFt>NO*E!~S*-uybxt>gw_6*EX z-lJt+A0JO~x$ehzQ|MYe`OzHe=Y0U4{|w^GEe63I$h6u$F1!5l>ds-+0V;7 zq5P$aDSx$Oew62C-rVEzkIMb{y;L^gj(UG@V%?MbGjHUtbM%z_>nCYnwWQu!N&Y#K z{CrXu%Rc)X))&>gB~|A2))a>lr@vI5lj6}=F>1UMllM+Jo!=|u?VOV+`S@rUI?1GKkpG}Q)(w?!Emfc69*(OeA4kI+Pmv3#&vBik(-%43Di=nyXo8PXpIj6% zST2sZLUNx;a!JHIxs>xC<>PeD{z!W|*>B6`5!Xpxl9_TPXTr)SBi@j!BHA^@)e$}A zQxUx-_Zcav?@qf^u8&yl>y7e1oT1zdH}Ersq+Bn#iE)&hDK8uN{{wt|xqN~9OX}a_ z^r{1hw^Cld6fs(EkGNh^@11f7^*CNGUyZ1D+$eWOeB`*}fyC6`SyH~Q+)cZjeuL9z z%0~Ld>Ge);lzg02HOF^pmwb=%au59}seiexm)xh(amRxy*ZbN5lIzzw9`E>G$8#O8 Gar{3>yH$Ju diff --git a/toolkit/crashreporter/google-breakpad/src/third_party/linux/lib/glog/libglog.a b/toolkit/crashreporter/google-breakpad/src/third_party/linux/lib/glog/libglog.a deleted file mode 100644 index 8d2afaef645de7ae6ba30a1b855f75343f689383..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 226928 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 z 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 core - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "google_breakpad/common/minidump_format.h" -#include "google_breakpad/common/minidump_cpu_x86.h" -#include "common/linux/linux_syscall_support.h" -#include "common/linux/minidump_format_linux.h" - -#if __WORDSIZE == 64 - #define ELF_CLASS ELFCLASS64 - #define Ehdr Elf64_Ehdr - #define Phdr Elf64_Phdr - #define Shdr Elf64_Shdr - #define Nhdr Elf64_Nhdr - #define auxv_t Elf64_auxv_t -#else - #define ELF_CLASS ELFCLASS32 - #define Ehdr Elf32_Ehdr - #define Phdr Elf32_Phdr - #define Shdr Elf32_Shdr - #define Nhdr Elf32_Nhdr - #define auxv_t Elf32_auxv_t -#endif - - -#if defined(__x86_64__) - #define ELF_ARCH EM_X86_64 -#elif defined(__i386__) - #define ELF_ARCH EM_386 -#elif defined(__ARM_ARCH_3__) - #define ELF_ARCH EM_ARM -#elif defined(__mips__) - #define ELF_ARCH EM_MIPS -#endif - -static int usage(const char* argv0) { - fprintf(stderr, "Usage: %s \n", argv0); - return 1; -} - -// Write all of the given buffer, handling short writes and EINTR. Return true -// iff successful. -static bool -writea(int fd, const void* idata, size_t length) { - const uint8_t* data = (const uint8_t*) idata; - - size_t done = 0; - while (done < length) { - ssize_t r; - do { - r = write(fd, data + done, length - done); - } while (r == -1 && errno == EINTR); - - if (r < 1) - return false; - done += r; - } - - return true; -} - -// A range of a mmaped file. -class MMappedRange { - public: - MMappedRange(const void* data, size_t length) - : data_(reinterpret_cast(data)), - length_(length) { - } - - // Get an object of |length| bytes at |offset| and return a pointer to it - // unless it's out of bounds. - const void* GetObject(size_t offset, size_t length) { - if (offset + length < offset) - return NULL; - if (offset + length > length_) - return NULL; - return data_ + offset; - } - - // Get element |index| of an array of objects of length |length| starting at - // |offset| bytes. Return NULL if out of bounds. - const void* GetArrayElement(size_t offset, size_t length, unsigned index) { - const size_t element_offset = offset + index * length; - return GetObject(element_offset, length); - } - - // Return a new range which is a subset of this range. - MMappedRange Subrange(const MDLocationDescriptor& location) const { - if (location.rva > length_ || - location.rva + location.data_size < location.rva || - location.rva + location.data_size > length_) { - return MMappedRange(NULL, 0); - } - - return MMappedRange(data_ + location.rva, location.data_size); - } - - const uint8_t* data() const { return data_; } - size_t length() const { return length_; } - - private: - const uint8_t* const data_; - const size_t length_; -}; - -/* Dynamically determines the byte sex of the system. Returns non-zero - * for big-endian machines. - */ -static inline int sex() { - int probe = 1; - return !*(char *)&probe; -} - -typedef struct elf_timeval { /* Time value with microsecond resolution */ - long tv_sec; /* Seconds */ - long tv_usec; /* Microseconds */ -} elf_timeval; - -typedef struct elf_siginfo { /* Information about signal (unused) */ - int32_t si_signo; /* Signal number */ - int32_t si_code; /* Extra code */ - int32_t si_errno; /* Errno */ -} elf_siginfo; - -typedef struct prstatus { /* Information about thread; includes CPU reg*/ - elf_siginfo pr_info; /* Info associated with signal */ - uint16_t pr_cursig; /* Current signal */ - unsigned long pr_sigpend; /* Set of pending signals */ - unsigned long pr_sighold; /* Set of held signals */ - pid_t pr_pid; /* Process ID */ - pid_t pr_ppid; /* Parent's process ID */ - pid_t pr_pgrp; /* Group ID */ - pid_t pr_sid; /* Session ID */ - elf_timeval pr_utime; /* User time */ - elf_timeval pr_stime; /* System time */ - elf_timeval pr_cutime; /* Cumulative user time */ - elf_timeval pr_cstime; /* Cumulative system time */ - user_regs_struct pr_reg; /* CPU registers */ - uint32_t pr_fpvalid; /* True if math co-processor being used */ -} prstatus; - -typedef struct prpsinfo { /* Information about process */ - unsigned char pr_state; /* Numeric process state */ - char pr_sname; /* Char for pr_state */ - unsigned char pr_zomb; /* Zombie */ - signed char pr_nice; /* Nice val */ - unsigned long pr_flag; /* Flags */ -#if defined(__x86_64__) || defined(__mips__) - uint32_t pr_uid; /* User ID */ - uint32_t pr_gid; /* Group ID */ -#else - uint16_t pr_uid; /* User ID */ - uint16_t pr_gid; /* Group ID */ -#endif - pid_t pr_pid; /* Process ID */ - pid_t pr_ppid; /* Parent's process ID */ - pid_t pr_pgrp; /* Group ID */ - pid_t pr_sid; /* Session ID */ - char pr_fname[16]; /* Filename of executable */ - char pr_psargs[80]; /* Initial part of arg list */ -} prpsinfo; - -// We parse the minidump file and keep the parsed information in this structure. -struct CrashedProcess { - CrashedProcess() - : crashing_tid(-1), - auxv(NULL), - auxv_length(0) { - memset(&prps, 0, sizeof(prps)); - prps.pr_sname = 'R'; - } - - struct Mapping { - uint64_t start_address, end_address; - }; - std::vector mappings; - - pid_t crashing_tid; - int fatal_signal; - - struct Thread { - pid_t tid; - user_regs_struct regs; - user_fpregs_struct fpregs; - user_fpxregs_struct fpxregs; - uintptr_t stack_addr; - const uint8_t* stack; - size_t stack_length; - }; - std::vector threads; - - const uint8_t* auxv; - size_t auxv_length; - - prpsinfo prps; -}; - -static uint32_t -U32(const uint8_t* data) { - uint32_t v; - memcpy(&v, data, sizeof(v)); - return v; -} - -static uint16_t -U16(const uint8_t* data) { - uint16_t v; - memcpy(&v, data, sizeof(v)); - return v; -} - -#if defined(__i386__) -static void -ParseThreadRegisters(CrashedProcess::Thread* thread, MMappedRange range) { - const MDRawContextX86* rawregs = - (const MDRawContextX86*) range.GetObject(0, sizeof(MDRawContextX86)); - - thread->regs.ebx = rawregs->ebx; - thread->regs.ecx = rawregs->ecx; - thread->regs.edx = rawregs->edx; - thread->regs.esi = rawregs->esi; - thread->regs.edi = rawregs->edi; - thread->regs.ebp = rawregs->ebp; - thread->regs.eax = rawregs->eax; - thread->regs.xds = rawregs->ds; - thread->regs.xes = rawregs->es; - thread->regs.xfs = rawregs->fs; - thread->regs.xgs = rawregs->gs; - thread->regs.orig_eax = rawregs->eax; - thread->regs.eip = rawregs->eip; - thread->regs.xcs = rawregs->cs; - thread->regs.eflags = rawregs->eflags; - thread->regs.esp = rawregs->esp; - thread->regs.xss = rawregs->ss; - - thread->fpregs.cwd = rawregs->float_save.control_word; - thread->fpregs.swd = rawregs->float_save.status_word; - thread->fpregs.twd = rawregs->float_save.tag_word; - thread->fpregs.fip = rawregs->float_save.error_offset; - thread->fpregs.fcs = rawregs->float_save.error_selector; - thread->fpregs.foo = rawregs->float_save.data_offset; - thread->fpregs.fos = rawregs->float_save.data_selector; - memcpy(thread->fpregs.st_space, rawregs->float_save.register_area, - 10 * 8); - - thread->fpxregs.cwd = rawregs->float_save.control_word; - thread->fpxregs.swd = rawregs->float_save.status_word; - thread->fpxregs.twd = rawregs->float_save.tag_word; - thread->fpxregs.fop = U16(rawregs->extended_registers + 6); - thread->fpxregs.fip = U16(rawregs->extended_registers + 8); - thread->fpxregs.fcs = U16(rawregs->extended_registers + 12); - thread->fpxregs.foo = U16(rawregs->extended_registers + 16); - thread->fpxregs.fos = U16(rawregs->extended_registers + 20); - thread->fpxregs.mxcsr = U32(rawregs->extended_registers + 24); - memcpy(thread->fpxregs.st_space, rawregs->extended_registers + 32, 128); - memcpy(thread->fpxregs.xmm_space, rawregs->extended_registers + 160, 128); -} -#else -#error "This code has not been ported to your platform yet" -#endif - -static void -ParseThreadList(CrashedProcess* crashinfo, MMappedRange range, - const MMappedRange& full_file) { - const uint32_t num_threads = - *(const uint32_t*) range.GetObject(0, sizeof(uint32_t)); - for (unsigned i = 0; i < num_threads; ++i) { - CrashedProcess::Thread thread; - memset(&thread, 0, sizeof(thread)); - const MDRawThread* rawthread = - (MDRawThread*) range.GetArrayElement(sizeof(uint32_t), - sizeof(MDRawThread), i); - thread.tid = rawthread->thread_id; - thread.stack_addr = rawthread->stack.start_of_memory_range; - MMappedRange stack_range = full_file.Subrange(rawthread->stack.memory); - thread.stack = stack_range.data(); - thread.stack_length = rawthread->stack.memory.data_size; - - ParseThreadRegisters(&thread, - full_file.Subrange(rawthread->thread_context)); - - crashinfo->threads.push_back(thread); - } -} - -static void -ParseAuxVector(CrashedProcess* crashinfo, MMappedRange range) { - crashinfo->auxv = range.data(); - crashinfo->auxv_length = range.length(); -} - -static void -ParseCmdLine(CrashedProcess* crashinfo, MMappedRange range) { - const char* cmdline = (const char*) range.data(); - for (size_t i = 0; i < range.length(); ++i) { - if (cmdline[i] == 0) { - static const size_t fname_len = sizeof(crashinfo->prps.pr_fname) - 1; - static const size_t args_len = sizeof(crashinfo->prps.pr_psargs) - 1; - memset(crashinfo->prps.pr_fname, 0, fname_len + 1); - memset(crashinfo->prps.pr_psargs, 0, args_len + 1); - const char* binary_name = strrchr(cmdline, '/'); - if (binary_name) { - binary_name++; - const unsigned len = strlen(binary_name); - memcpy(crashinfo->prps.pr_fname, binary_name, - len > fname_len ? fname_len : len); - } else { - memcpy(crashinfo->prps.pr_fname, cmdline, - i > fname_len ? fname_len : i); - } - - const unsigned len = range.length() > args_len ? - args_len : range.length(); - memcpy(crashinfo->prps.pr_psargs, cmdline, len); - for (unsigned i = 0; i < len; ++i) { - if (crashinfo->prps.pr_psargs[i] == 0) - crashinfo->prps.pr_psargs[i] = ' '; - } - } - } -} - -static void -ParseExceptionStream(CrashedProcess* crashinfo, MMappedRange range) { - const MDRawExceptionStream* exp = - (MDRawExceptionStream*) range.GetObject(0, sizeof(MDRawExceptionStream)); - crashinfo->crashing_tid = exp->thread_id; - crashinfo->fatal_signal = (int) exp->exception_record.exception_code; -} - -static bool -WriteThread(const CrashedProcess::Thread& thread, int fatal_signal) { - struct prstatus pr; - memset(&pr, 0, sizeof(pr)); - - pr.pr_info.si_signo = fatal_signal; - pr.pr_cursig = fatal_signal; - pr.pr_pid = thread.tid; - memcpy(&pr.pr_reg, &thread.regs, sizeof(user_regs_struct)); - - Nhdr nhdr; - memset(&nhdr, 0, sizeof(nhdr)); - nhdr.n_namesz = 5; - nhdr.n_descsz = sizeof(struct prstatus); - nhdr.n_type = NT_PRSTATUS; - if (!writea(1, &nhdr, sizeof(nhdr)) || - !writea(1, "CORE\0\0\0\0", 8) || - !writea(1, &pr, sizeof(struct prstatus))) { - return false; - } - - nhdr.n_descsz = sizeof(user_fpregs_struct); - nhdr.n_type = NT_FPREGSET; - if (!writea(1, &nhdr, sizeof(nhdr)) || - !writea(1, "CORE\0\0\0\0", 8) || - !writea(1, &thread.fpregs, sizeof(user_fpregs_struct))) { - return false; - } - - nhdr.n_descsz = sizeof(user_fpxregs_struct); - nhdr.n_type = NT_PRXFPREG; - if (!writea(1, &nhdr, sizeof(nhdr)) || - !writea(1, "LINUX\0\0\0", 8) || - !writea(1, &thread.fpxregs, sizeof(user_fpxregs_struct))) { - return false; - } - - return true; -} - -static void -ParseModuleStream(CrashedProcess* crashinfo, MMappedRange range) { - const uint32_t num_mappings = - *(const uint32_t*) range.GetObject(0, sizeof(uint32_t)); - for (unsigned i = 0; i < num_mappings; ++i) { - CrashedProcess::Mapping mapping; - const MDRawModule* rawmodule = - (MDRawModule*) range.GetArrayElement(sizeof(uint32_t), - MD_MODULE_SIZE, i); - mapping.start_address = rawmodule->base_of_image; - mapping.end_address = rawmodule->size_of_image + rawmodule->base_of_image; - - crashinfo->mappings.push_back(mapping); - } -} - -int -main(int argc, char** argv) { - if (argc != 2) - return usage(argv[0]); - - const int fd = open(argv[1], O_RDONLY); - if (fd < 0) - return usage(argv[0]); - - struct stat st; - fstat(fd, &st); - - const void* bytes = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); - close(fd); - if (bytes == MAP_FAILED) { - perror("Failed to mmap dump file"); - return 1; - } - - MMappedRange dump(bytes, st.st_size); - - const MDRawHeader* header = - (const MDRawHeader*) dump.GetObject(0, sizeof(MDRawHeader)); - - CrashedProcess crashinfo; - - for (unsigned i = 0; i < header->stream_count; ++i) { - const MDRawDirectory* dirent = - (const MDRawDirectory*) dump.GetArrayElement( - header->stream_directory_rva, sizeof(MDRawDirectory), i); - switch (dirent->stream_type) { - case MD_THREAD_LIST_STREAM: - ParseThreadList(&crashinfo, dump.Subrange(dirent->location), dump); - break; - case MD_LINUX_AUXV: - ParseAuxVector(&crashinfo, dump.Subrange(dirent->location)); - break; - case MD_LINUX_CMD_LINE: - ParseCmdLine(&crashinfo, dump.Subrange(dirent->location)); - break; - case MD_EXCEPTION_STREAM: - ParseExceptionStream(&crashinfo, dump.Subrange(dirent->location)); - break; - case MD_MODULE_LIST_STREAM: - ParseModuleStream(&crashinfo, dump.Subrange(dirent->location)); - default: - fprintf(stderr, "Skipping %x\n", dirent->stream_type); - } - } - - // Write the ELF header. The file will look like: - // ELF header - // Phdr for the PT_NOTE - // Phdr for each of the thread stacks - // PT_NOTE - // each of the thread stacks - Ehdr ehdr; - memset(&ehdr, 0, sizeof(Ehdr)); - ehdr.e_ident[0] = ELFMAG0; - ehdr.e_ident[1] = ELFMAG1; - ehdr.e_ident[2] = ELFMAG2; - ehdr.e_ident[3] = ELFMAG3; - ehdr.e_ident[4] = ELF_CLASS; - ehdr.e_ident[5] = sex() ? ELFDATA2MSB : ELFDATA2LSB; - ehdr.e_ident[6] = EV_CURRENT; - ehdr.e_type = ET_CORE; - ehdr.e_machine = ELF_ARCH; - ehdr.e_version = EV_CURRENT; - ehdr.e_phoff = sizeof(Ehdr); - ehdr.e_ehsize = sizeof(Ehdr); - ehdr.e_phentsize= sizeof(Phdr); - ehdr.e_phnum = 1 + crashinfo.threads.size() + crashinfo.mappings.size(); - ehdr.e_shentsize= sizeof(Shdr); - if (!writea(1, &ehdr, sizeof(Ehdr))) - return 1; - - size_t offset = sizeof(Ehdr) + - (1 + crashinfo.threads.size() + - crashinfo.mappings.size()) * sizeof(Phdr); - size_t filesz = sizeof(Nhdr) + 8 + sizeof(prpsinfo) + - // sizeof(Nhdr) + 8 + sizeof(user) + - sizeof(Nhdr) + 8 + crashinfo.auxv_length + - crashinfo.threads.size() * ( - (sizeof(Nhdr) + 8 + sizeof(prstatus)) + - sizeof(Nhdr) + 8 + sizeof(user_fpregs_struct) + - sizeof(Nhdr) + 8 + sizeof(user_fpxregs_struct)); - - Phdr phdr; - memset(&phdr, 0, sizeof(Phdr)); - phdr.p_type = PT_NOTE; - phdr.p_offset = offset; - phdr.p_filesz = filesz; - if (!writea(1, &phdr, sizeof(phdr))) - return 1; - - phdr.p_type = PT_LOAD; - phdr.p_align = getpagesize(); - size_t note_align = phdr.p_align - ((offset+filesz) % phdr.p_align); - if (note_align == phdr.p_align) - note_align = 0; - offset += note_align; - - for (unsigned i = 0; i < crashinfo.threads.size(); ++i) { - const CrashedProcess::Thread& thread = crashinfo.threads[i]; - offset += filesz; - filesz = thread.stack_length; - phdr.p_offset = offset; - phdr.p_vaddr = thread.stack_addr; - phdr.p_filesz = phdr.p_memsz = filesz; - phdr.p_flags = PF_R | PF_W; - if (!writea(1, &phdr, sizeof(phdr))) - return 1; - } - - for (unsigned i = 0; i < crashinfo.mappings.size(); ++i) { - const CrashedProcess::Mapping& mapping = crashinfo.mappings[i]; - phdr.p_offset = 0; - phdr.p_vaddr = mapping.start_address; - phdr.p_filesz = 0; - phdr.p_flags = PF_R; - phdr.p_memsz = mapping.end_address - mapping.start_address; - if (!writea(1, &phdr, sizeof(phdr))) - return 1; - } - - Nhdr nhdr; - memset(&nhdr, 0, sizeof(nhdr)); - nhdr.n_namesz = 5; - nhdr.n_descsz = sizeof(prpsinfo); - nhdr.n_type = NT_PRPSINFO; - if (!writea(1, &nhdr, sizeof(nhdr)) || - !writea(1, "CORE\0\0\0\0", 8) || - !writea(1, &crashinfo.prps, sizeof(prpsinfo))) { - return 1; - } - - nhdr.n_descsz = crashinfo.auxv_length; - nhdr.n_type = NT_AUXV; - if (!writea(1, &nhdr, sizeof(nhdr)) || - !writea(1, "CORE\0\0\0\0", 8) || - !writea(1, &crashinfo.auxv, crashinfo.auxv_length)) { - return 1; - } - - for (unsigned i = 0; i < crashinfo.threads.size(); ++i) { - if (crashinfo.threads[i].tid == crashinfo.crashing_tid) { - WriteThread(crashinfo.threads[i], crashinfo.fatal_signal); - break; - } - } - - for (unsigned i = 0; i < crashinfo.threads.size(); ++i) { - if (crashinfo.threads[i].tid != crashinfo.crashing_tid) - WriteThread(crashinfo.threads[i], 0); - } - - if (note_align) { - char scratch[note_align]; - memset(scratch, 0, sizeof(scratch)); - if (!writea(1, scratch, sizeof(scratch))) - return 1; - } - - for (unsigned i = 0; i < crashinfo.threads.size(); ++i) { - const CrashedProcess::Thread& thread = crashinfo.threads[i]; - if (!writea(1, thread.stack, thread.stack_length)) - return 1; - } - - munmap(const_cast(bytes), st.st_size); - - return 0; -} diff --git a/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/crash_report.mm b/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/crash_report.mm index 22d2a18e7c0..f90ee42ff68 100644 --- a/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/crash_report.mm +++ b/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/crash_report.mm @@ -59,7 +59,6 @@ using google_breakpad::BasicSourceLineResolver; using google_breakpad::CallStack; using google_breakpad::CodeModule; using google_breakpad::CodeModules; -using google_breakpad::Minidump; using google_breakpad::MinidumpProcessor; using google_breakpad::OnDemandSymbolSupplier; using google_breakpad::PathnameStripper; @@ -74,7 +73,6 @@ typedef struct { NSString *minidumpPath; NSString *searchDir; NSString *symbolSearchDir; - BOOL printThreadMemory; } Options; //============================================================================= @@ -222,8 +220,10 @@ static void PrintModules(const CodeModules *modules) { } } -static void ProcessSingleReport(Options *options, NSString *file_path) { - string minidump_file([file_path fileSystemRepresentation]); +//============================================================================= +static void Start(Options *options) { + string minidump_file([options->minidumpPath fileSystemRepresentation]); + BasicSourceLineResolver resolver; string search_dir = options->searchDir ? [options->searchDir fileSystemRepresentation] : ""; @@ -234,14 +234,8 @@ static void ProcessSingleReport(Options *options, NSString *file_path) { scoped_ptr minidump_processor(new MinidumpProcessor(symbol_supplier.get(), &resolver)); ProcessState process_state; - scoped_ptr dump(new google_breakpad::Minidump(minidump_file)); - - if (!dump->Read()) { - fprintf(stderr, "Minidump %s could not be read\n", dump->path().c_str()); - return; - } - if (minidump_processor->Process(dump.get(), &process_state) != - google_breakpad::PROCESS_OK) { + if (minidump_processor->Process(minidump_file, &process_state) != + MinidumpProcessor::PROCESS_OK) { fprintf(stderr, "MinidumpProcessor::Process failed\n"); return; } @@ -280,20 +274,12 @@ static void ProcessSingleReport(Options *options, NSString *file_path) { // Print all of the threads in the dump. int thread_count = process_state.threads()->size(); - const std::vector - *thread_memory_regions = process_state.thread_memory_regions(); - for (int thread_index = 0; thread_index < thread_count; ++thread_index) { if (thread_index != requesting_thread) { // Don't print the crash thread again, it was already printed. printf("\n"); printf("Thread %d\n", thread_index); PrintStack(process_state.threads()->at(thread_index), cpu); - google_breakpad::MinidumpMemoryRegion *thread_stack_bytes = - thread_memory_regions->at(thread_index); - if (options->printThreadMemory) { - thread_stack_bytes->Print(); - } } } @@ -302,36 +288,11 @@ static void ProcessSingleReport(Options *options, NSString *file_path) { printf("\nThread %d:", requesting_thread); PrintRegisters(process_state.threads()->at(requesting_thread), cpu); } - + // Print information about modules PrintModules(process_state.modules()); } -//============================================================================= -static void Start(Options *options) { - NSFileManager *manager = [NSFileManager defaultManager]; - NSString *minidump_path = options->minidumpPath; - BOOL is_dir = NO; - BOOL file_exists = [manager fileExistsAtPath:minidump_path - isDirectory:&is_dir]; - if (file_exists && is_dir) { - NSDirectoryEnumerator *enumerator = - [manager enumeratorAtPath:minidump_path]; - NSString *current_file = nil; - while ((current_file = [enumerator nextObject])) { - if ([[current_file pathExtension] isEqualTo:@"dmp"]) { - printf("Attempting to process report: %s\n", - [current_file cStringUsingEncoding:NSASCIIStringEncoding]); - NSString *full_path = - [minidump_path stringByAppendingPathComponent:current_file]; - ProcessSingleReport(options, full_path); - } - } - } else if (file_exists) { - ProcessSingleReport(options, minidump_path); - } -} - //============================================================================= static void Usage(int argc, const char *argv[]) { fprintf(stderr, "Convert a minidump to a crash report. Breakpad symbol " @@ -342,11 +303,9 @@ static void Usage(int argc, const char *argv[]) { "If modules cannot be found at the paths stored in the " "minidump file, they will be searched for at " "/.\n"); - fprintf(stderr, "Usage: %s [-s module-search-dir] [-S symbol-file-search-dir] " - "minidump-file\n", argv[0]); + fprintf(stderr, "Usage: %s [-s module-search-dir] [-S symbol-file-search-dir] minidump-file\n", argv[0]); fprintf(stderr, "\t-s: Specify a search directory to use for missing modules\n" - "\t-S: Specify a search directory to use for symbol files\n" - "\t-t: Print thread stack memory in hex\n" + "\t-S: Specify a search directory to use for symbol files\n" "\t-h: Usage\n" "\t-?: Usage\n"); } @@ -356,7 +315,7 @@ static void SetupOptions(int argc, const char *argv[], Options *options) { extern int optind; char ch; - while ((ch = getopt(argc, (char * const *)argv, "S:s:ht?")) != -1) { + while ((ch = getopt(argc, (char * const *)argv, "S:s:h?")) != -1) { switch (ch) { case 's': options->searchDir = [[NSFileManager defaultManager] @@ -370,9 +329,6 @@ static void SetupOptions(int argc, const char *argv[], Options *options) { length:strlen(optarg)]; break; - case 't': - options->printThreadMemory = YES; - break; case 'h': case '?': Usage(argc, argv); diff --git a/toolkit/toolkit-makefiles.sh b/toolkit/toolkit-makefiles.sh index 0c434094e4a..adc9416e32f 100644 --- a/toolkit/toolkit-makefiles.sh +++ b/toolkit/toolkit-makefiles.sh @@ -670,7 +670,6 @@ MAKEFILES_xulapp=" toolkit/crashreporter/client/Makefile toolkit/crashreporter/google-breakpad/src/client/Makefile toolkit/crashreporter/google-breakpad/src/client/linux/handler/Makefile - toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/Makefile toolkit/crashreporter/google-breakpad/src/client/mac/handler/Makefile toolkit/crashreporter/google-breakpad/src/client/solaris/handler/Makefile toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/Makefile diff --git a/toolkit/xre/Makefile.in b/toolkit/xre/Makefile.in index 619997af933..eab29087bd4 100644 --- a/toolkit/xre/Makefile.in +++ b/toolkit/xre/Makefile.in @@ -151,7 +151,6 @@ endif ifeq ($(OS_ARCH),Linux) SHARED_LIBRARY_LIBS += \ $(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/linux/handler/$(LIB_PREFIX)exception_handler_s.$(LIB_SUFFIX) \ - $(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/$(LIB_PREFIX)minidump_writer_s.$(LIB_SUFFIX) \ $(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/$(LIB_PREFIX)minidump_file_writer_s.$(LIB_SUFFIX) \ $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/$(LIB_PREFIX)breakpad_common_s.$(LIB_SUFFIX) \ $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/linux/$(LIB_PREFIX)breakpad_linux_common_s.$(LIB_SUFFIX) \ From d617d40d9b3bdca2aed1392c0a2992bc55b12b9a Mon Sep 17 00:00:00 2001 From: Benjamin Smedberg Date: Wed, 16 Dec 2009 13:52:11 -0500 Subject: [PATCH 05/14] e10s merge bits snuck in again :-( --- toolkit/toolkit-tiers.mk | 1 - 1 file changed, 1 deletion(-) diff --git a/toolkit/toolkit-tiers.mk b/toolkit/toolkit-tiers.mk index d1b1f8f79c1..012f9153033 100644 --- a/toolkit/toolkit-tiers.mk +++ b/toolkit/toolkit-tiers.mk @@ -85,7 +85,6 @@ tier_gecko_dirs += ipc endif tier_gecko_dirs += \ - $(tier_necko_dirs) \ js/src/xpconnect \ js/ctypes \ intl/chardet \ From 8e001bc89097d1927779ddb4d15d3b06f3c157ed Mon Sep 17 00:00:00 2001 From: Dietrich Ayala Date: Wed, 16 Dec 2009 11:02:10 -0800 Subject: [PATCH 06/14] Backout of bug 532147 and bug 532146 due to test failure. --- netwerk/base/src/NetUtil.jsm | 60 ++--------- netwerk/test/unit/test_NetUtil.js | 173 +----------------------------- 2 files changed, 12 insertions(+), 221 deletions(-) diff --git a/netwerk/base/src/NetUtil.jsm b/netwerk/base/src/NetUtil.jsm index 46381237ae4..d620d2324cc 100644 --- a/netwerk/base/src/NetUtil.jsm +++ b/netwerk/base/src/NetUtil.jsm @@ -128,23 +128,23 @@ const NetUtil = { }, /** - * Asynchronously opens a source and fetches the response. A source can be - * an nsIURI, nsIFile, string spec, or nsIChannel. The provided callback - * will get an input stream containing the response, and the result code. + * Asynchronously opens a channel and fetches the response. The provided + * callback will get an input stream containing the response, and the result + * code. * - * @param aSource - * The nsIURI, nsIFile, string spec, or nsIChannel to open. + * @param aChannel + * The nsIChannel to open. * @param aCallback * The callback function that will be notified upon completion. It * will get two arguments: * 1) An nsIInputStream containing the data from the channel, if any. - * 2) The status code from opening the source. + * 2) The status code from opening the channel. */ - asyncFetch: function NetUtil_asyncOpen(aSource, aCallback) + asyncFetch: function NetUtil_asyncOpen(aChannel, aCallback) { - if (!aSource || !aCallback) { + if (!aChannel || !aCallback) { let exception = new Components.Exception( - "Must have a source and a callback", + "Must have a channel and a callback", Cr.NS_ERROR_INVALID_ARG, Components.stack.caller ); @@ -168,12 +168,7 @@ const NetUtil = { } }); - let channel = aSource; - if (!(channel instanceof Ci.nsIChannel)) { - channel = this.newChannel(aSource); - } - - channel.asyncOpen(listener, null); + aChannel.asyncOpen(listener, null); }, /** @@ -209,41 +204,6 @@ const NetUtil = { return this.ioService.newURI(aTarget, aOriginCharset, aBaseURI); }, - /** - * Constructs a new channel for the given spec, character set, and base URI, - * or nsIURI, or nsIFile. - * - * @param aWhatToLoad - * The string spec for the desired URI, an nsIURI, or an nsIFile. - * @param aOriginCharset [optional] - * The character set for the URI. Only used if aWhatToLoad is a - * string. - * @param aBaseURI [optional] - * The base URI for the spec. Only used if aWhatToLoad is a string. - * - * @return an nsIChannel object. - */ - newChannel: function NetUtil_newChannel(aWhatToLoad, aOriginCharset, - aBaseURI) - { - if (!aWhatToLoad) { - let exception = new Components.Exception( - "Must have a non-null string spec, nsIURI, or nsIFile object", - Cr.NS_ERROR_INVALID_ARG, - Components.stack.caller - ); - throw exception; - } - - let uri = aWhatToLoad; - if (!(aWhatToLoad instanceof Ci.nsIURI)) { - // We either have a string or an nsIFile that we'll need a URI for. - uri = this.newURI(aWhatToLoad, aOriginCharset, aBaseURI); - } - - return this.ioService.newChannelFromURI(uri); - }, - /** * Returns a reference to nsIIOService. * diff --git a/netwerk/test/unit/test_NetUtil.js b/netwerk/test/unit/test_NetUtil.js index 5d282426455..670e112cb41 100644 --- a/netwerk/test/unit/test_NetUtil.js +++ b/netwerk/test/unit/test_NetUtil.js @@ -224,7 +224,7 @@ function test_asyncFetch_no_callback() run_next_test(); } -function test_asyncFetch_with_nsIChannel() +function test_asyncFetch() { const TEST_DATA = "this is a test string"; @@ -258,110 +258,6 @@ function test_asyncFetch_with_nsIChannel() }); } -function test_asyncFetch_with_nsIURI() -{ - const TEST_DATA = "this is a test string"; - - // Start the http server, and register our handler. - let server = new nsHttpServer(); - server.registerPathHandler("/test", function(aRequest, aResponse) { - aResponse.setStatusLine(aRequest.httpVersion, 200, "OK"); - aResponse.setHeader("Content-Type", "text/plain", false); - aResponse.write(TEST_DATA); - }); - server.start(4444); - - // Create our URI. - let uri = NetUtil.newURI("http://localhost:4444/test"); - - // Open our URI asynchronously. - NetUtil.asyncFetch(uri, function(aInputStream, aResult) { - // Check that we had success. - do_check_true(Components.isSuccessCode(aResult)); - - // Check that we got the right data. - do_check_eq(aInputStream.available(), TEST_DATA.length); - let is = Cc["@mozilla.org/scriptableinputstream;1"]. - createInstance(Ci.nsIScriptableInputStream); - is.init(aInputStream); - let result = is.read(TEST_DATA.length); - do_check_eq(TEST_DATA, result); - - server.stop(run_next_test); - }); -} - -function test_asyncFetch_with_string() -{ - const TEST_DATA = "this is a test string"; - - // Start the http server, and register our handler. - let server = new nsHttpServer(); - server.registerPathHandler("/test", function(aRequest, aResponse) { - aResponse.setStatusLine(aRequest.httpVersion, 200, "OK"); - aResponse.setHeader("Content-Type", "text/plain", false); - aResponse.write(TEST_DATA); - }); - server.start(4444); - - // Open our location asynchronously. - NetUtil.asyncFetch("http://localhost:4444/test", function(aInputStream, - aResult) { - // Check that we had success. - do_check_true(Components.isSuccessCode(aResult)); - - // Check that we got the right data. - do_check_eq(aInputStream.available(), TEST_DATA.length); - let is = Cc["@mozilla.org/scriptableinputstream;1"]. - createInstance(Ci.nsIScriptableInputStream); - is.init(aInputStream); - let result = is.read(TEST_DATA.length); - do_check_eq(TEST_DATA, result); - - server.stop(run_next_test); - }); -} - -function test_asyncFetch_with_nsIFile() -{ - const TEST_DATA = "this is a test string"; - - // First we need a file to read from. - let file = Cc["@mozilla.org/file/directory_service;1"]. - getService(Ci.nsIProperties). - get("TmpD", Ci.nsIFile); - file.append("NetUtil-asyncFetch-test-file.tmp"); - file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666); - - // Write the test data to the file. - let ostream = Cc["@mozilla.org/network/file-output-stream;1"]. - createInstance(Ci.nsIFileOutputStream); - ostream.init(file, -1, -1, 0); - ostream.write(TEST_DATA, TEST_DATA.length); - - // Sanity check to make sure the data was written. - do_check_eq(TEST_DATA, getFileContents(file)); - - // Open our file asynchronously. - NetUtil.asyncFetch(file, function(aInputStream, aResult) { - // Check that we had success. - do_check_true(Components.isSuccessCode(aResult)); - - // Check that we got the right data. - do_check_eq(aInputStream.available(), TEST_DATA.length); - let is = Cc["@mozilla.org/scriptableinputstream;1"]. - createInstance(Ci.nsIScriptableInputStream); - is.init(aInputStream); - let result = is.read(TEST_DATA.length); - do_check_eq(TEST_DATA, result); - - // Remove our test file. - file.remove(false); - - run_next_test(); - }); -} - function test_asyncFetch_does_not_block() { // Create our channel that has no data. @@ -390,64 +286,6 @@ function test_asyncFetch_does_not_block() }); } -function test_newChannel_no_specifier() -{ - try { - NetUtil.newChannel(); - do_throw("should throw!"); - } - catch (e) { - do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG); - } - - run_next_test(); -} - -function test_newChannel_with_string() -{ - const TEST_SPEC = "http://mozilla.org"; - - // Check that we get the same URI back from channel the IO service creates and - // the channel the utility method creates. - let ios = NetUtil.ioService; - let iosChannel = ios.newChannel(TEST_SPEC, null, null); - let NetUtilChannel = NetUtil.newChannel(TEST_SPEC); - do_check_true(iosChannel.URI.equals(NetUtilChannel.URI)); - - run_next_test(); -} - -function test_newChannel_with_nsIURI() -{ - const TEST_SPEC = "http://mozilla.org"; - - // Check that we get the same URI back from channel the IO service creates and - // the channel the utility method creates. - let uri = NetUtil.newURI(TEST_SPEC); - let iosChannel = NetUtil.ioService.newChannelFromURI(uri); - let NetUtilChannel = NetUtil.newChannel(uri); - do_check_true(iosChannel.URI.equals(NetUtilChannel.URI)); - - run_next_test(); -} - -function test_newChannel_with_nsIFile() -{ - let file = Cc["@mozilla.org/file/directory_service;1"]. - getService(Ci.nsIProperties). - get("TmpD", Ci.nsIFile); - file.append("NetUtil-test-file.tmp"); - - // Check that we get the same URI back from channel the IO service creates and - // the channel the utility method creates. - let uri = NetUtil.newURI(file); - let iosChannel = NetUtil.ioService.newChannelFromURI(uri); - let NetUtilChannel = NetUtil.newChannel(uri); - do_check_true(iosChannel.URI.equals(NetUtilChannel.URI)); - - run_next_test(); -} - //////////////////////////////////////////////////////////////////////////////// //// Test Runner @@ -460,15 +298,8 @@ let tests = [ test_ioService, test_asyncFetch_no_channel, test_asyncFetch_no_callback, - test_asyncFetch_with_nsIChannel, - test_asyncFetch_with_nsIURI, - test_asyncFetch_with_string, - test_asyncFetch_with_nsIFile, + test_asyncFetch, test_asyncFetch_does_not_block, - test_newChannel_no_specifier, - test_newChannel_with_string, - test_newChannel_with_nsIURI, - test_newChannel_with_nsIFile, ]; let index = 0; From dec756545727d30d9855f4d87395c320e041eb73 Mon Sep 17 00:00:00 2001 From: Benjamin Smedberg Date: Wed, 16 Dec 2009 15:08:45 -0500 Subject: [PATCH 07/14] Bug 532751 - Notify the nsNPAPIPlugin and the related nsNPAPIPluginInstances when a plugin crashes so that reloading will create a new plugin process, r=josh --- dom/plugins/PluginLibrary.h | 10 +++++- dom/plugins/PluginModuleParent.cpp | 14 +++++++-- dom/plugins/PluginModuleParent.h | 7 ++++- dom/plugins/PluginPRLibrary.h | 2 ++ modules/plugin/base/src/nsNPAPIPlugin.cpp | 10 ++++++ modules/plugin/base/src/nsNPAPIPlugin.h | 6 ++++ modules/plugin/base/src/nsPluginHost.cpp | 38 +++++++++++++++++++++++ modules/plugin/base/src/nsPluginHost.h | 6 +++- modules/plugin/test/mochitest/Makefile.in | 12 +++---- 9 files changed, 93 insertions(+), 12 deletions(-) diff --git a/dom/plugins/PluginLibrary.h b/dom/plugins/PluginLibrary.h index 2adec812c26..d8838442430 100644 --- a/dom/plugins/PluginLibrary.h +++ b/dom/plugins/PluginLibrary.h @@ -1,4 +1,4 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: sw=4 ts=4 et : * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -43,6 +43,8 @@ #include "npapi.h" #include "nscore.h" +class nsNPAPIPlugin; + namespace mozilla { class PluginLibrary @@ -50,6 +52,12 @@ class PluginLibrary public: virtual ~PluginLibrary() { } + /** + * Inform this library about the nsNPAPIPlugin which owns it. This + * object will hold a weak pointer to the plugin. + */ + virtual void SetPlugin(nsNPAPIPlugin* plugin) = 0; + virtual bool HasRequiredFunctions() = 0; #if defined(XP_UNIX) && !defined(XP_MACOSX) diff --git a/dom/plugins/PluginModuleParent.cpp b/dom/plugins/PluginModuleParent.cpp index 32a5354829f..b6a43cfca7c 100644 --- a/dom/plugins/PluginModuleParent.cpp +++ b/dom/plugins/PluginModuleParent.cpp @@ -69,6 +69,7 @@ PluginModuleParent::PluginModuleParent(const char* aFilePath) : mSubprocess(new PluginProcessParent(aFilePath)) , mShutdown(false) , mNPNIface(NULL) + , mPlugin(NULL) { NS_ASSERTION(mSubprocess, "Out of memory!"); @@ -97,9 +98,16 @@ PluginModuleParent::ActorDestroy(ActorDestroyReason why) { switch (why) { case AbnormalShutdown: - // TODObsmedberg: notify the plugin host to forget this plugin module - // and instantiate us again. - // FALL THROUGH + mShutdown = true; + // Defer the PluginCrashed method so that we don't re-enter + // and potentially modify the actor child list while enumerating it. + if (mPlugin) { + nsCOMPtr r = + new nsRunnableMethod( + mPlugin, &nsNPAPIPlugin::PluginCrashed); + NS_DispatchToMainThread(r); + } + break; case NormalShutdown: mShutdown = true; diff --git a/dom/plugins/PluginModuleParent.h b/dom/plugins/PluginModuleParent.h index 183b44a2395..302e047f5cb 100644 --- a/dom/plugins/PluginModuleParent.h +++ b/dom/plugins/PluginModuleParent.h @@ -97,9 +97,13 @@ protected: public: PluginModuleParent(const char* aFilePath); - virtual ~PluginModuleParent(); + NS_OVERRIDE virtual void SetPlugin(nsNPAPIPlugin* plugin) + { + mPlugin = plugin; + } + NS_OVERRIDE virtual void ActorDestroy(ActorDestroyReason why); /** @@ -213,6 +217,7 @@ private: bool mShutdown; const NPNetscapeFuncs* mNPNIface; nsTHashtable mValidIdentifiers; + nsNPAPIPlugin* mPlugin; }; } // namespace plugins diff --git a/dom/plugins/PluginPRLibrary.h b/dom/plugins/PluginPRLibrary.h index 649ee0189f0..8a199ac73e3 100644 --- a/dom/plugins/PluginPRLibrary.h +++ b/dom/plugins/PluginPRLibrary.h @@ -83,6 +83,8 @@ public: // unref here?? } + virtual void SetPlugin(nsNPAPIPlugin*) { } + virtual bool HasRequiredFunctions() { mNP_Initialize = (NP_InitializeFunc) PR_FindFunctionSymbol(mLibrary, "NP_Initialize"); diff --git a/modules/plugin/base/src/nsNPAPIPlugin.cpp b/modules/plugin/base/src/nsNPAPIPlugin.cpp index daab22b5e69..e138b53d6c1 100644 --- a/modules/plugin/base/src/nsNPAPIPlugin.cpp +++ b/modules/plugin/base/src/nsNPAPIPlugin.cpp @@ -280,6 +280,7 @@ nsNPAPIPlugin::nsNPAPIPlugin(NPPluginFuncs* callbacks, #endif fLibrary = aLibrary; + fLibrary->SetPlugin(this); } nsNPAPIPlugin::~nsNPAPIPlugin() @@ -299,6 +300,15 @@ nsNPAPIPlugin::SetPluginRefNum(short aRefNum) } #endif +#ifdef MOZ_IPC +void +nsNPAPIPlugin::PluginCrashed() +{ + nsRefPtr host = dont_AddRef(nsPluginHost::GetInst()); + host->PluginCrashed(this); +} +#endif + namespace { #ifdef MOZ_IPC diff --git a/modules/plugin/base/src/nsNPAPIPlugin.h b/modules/plugin/base/src/nsNPAPIPlugin.h index a2dc127b096..51e492a2277 100644 --- a/modules/plugin/base/src/nsNPAPIPlugin.h +++ b/modules/plugin/base/src/nsNPAPIPlugin.h @@ -94,6 +94,12 @@ public: void SetPluginRefNum(short aRefNum); #endif +#ifdef MOZ_IPC + // The IPC mechanism notifies the nsNPAPIPlugin if the plugin crashes and is + // no longer usable. + void PluginCrashed(); +#endif + protected: // Ensures that the static CALLBACKS is properly initialized static void CheckClassInitialized(void); diff --git a/modules/plugin/base/src/nsPluginHost.cpp b/modules/plugin/base/src/nsPluginHost.cpp index da3d7e77777..16bf88ddffc 100644 --- a/modules/plugin/base/src/nsPluginHost.cpp +++ b/modules/plugin/base/src/nsPluginHost.cpp @@ -5180,6 +5180,44 @@ NS_IMETHODIMP nsPluginHost::Notify(nsITimer* timer) return NS_ERROR_FAILURE; } +#ifdef MOZ_IPC +void +nsPluginHost::PluginCrashed(nsNPAPIPlugin* aPlugin) +{ + // Find the nsPluginTag corresponding to this plugin + + nsPluginTag* plugin; + for (plugin = mPlugins; plugin; plugin = plugin->mNext) { + if (plugin->mEntryPoint == aPlugin) + break; + } + if (!plugin) { + NS_WARNING("nsPluginTag not found in nsPluginHost::PluginCrashed"); + return; + } + + // Invalidate each nsPluginInstanceTag for the crashed plugin + + nsPluginInstanceTag** pinstancetag = &mPluginInstanceTagList.mFirst; + while (*pinstancetag) { + nsPluginInstanceTag* instancetag = *pinstancetag; + if (instancetag->mPluginTag == plugin) { + *pinstancetag = (*pinstancetag)->mNext; + delete instancetag; + } + else { + pinstancetag = &(*pinstancetag)->mNext; + } + } + + // Only after all instances have been invalidated is it safe to null + // out nsPluginTag.mEntryPoint. The next time we try to create an + // instance of this plugin we reload it (launch a new plugin process). + + plugin->mEntryPoint = nsnull; +} +#endif + nsresult nsPluginStreamListenerPeer::ServeStreamAsFile(nsIRequest *request, nsISupports* aContext) { diff --git a/modules/plugin/base/src/nsPluginHost.h b/modules/plugin/base/src/nsPluginHost.h index e1640d82995..6a7627e1fa9 100644 --- a/modules/plugin/base/src/nsPluginHost.h +++ b/modules/plugin/base/src/nsPluginHost.h @@ -161,7 +161,11 @@ public: void AddIdleTimeTarget(nsIPluginInstanceOwner* objectFrame, PRBool isVisible); void RemoveIdleTimeTarget(nsIPluginInstanceOwner* objectFrame); - + +#ifdef MOZ_IPC + void PluginCrashed(nsNPAPIPlugin* plugin); +#endif + private: nsresult TrySetUpPluginInstance(const char *aMimeType, nsIURI *aURL, nsIPluginInstanceOwner *aOwner); diff --git a/modules/plugin/test/mochitest/Makefile.in b/modules/plugin/test/mochitest/Makefile.in index d1be0e2421c..dd9e1404e10 100644 --- a/modules/plugin/test/mochitest/Makefile.in +++ b/modules/plugin/test/mochitest/Makefile.in @@ -69,12 +69,12 @@ _MOCHITEST_FILES = \ # test_npruntime_npnsetexception.html \ Disabled for e10s -#ifdef MOZ_IPC -#_MOCHITEST_FILES += \ -# test_crashing.html \ -# crashing_subpage.html \ -# $(NULL) -#endif +ifdef MOZ_IPC +_MOCHITEST_FILES += \ + test_crashing.html \ + crashing_subpage.html \ + $(NULL) +endif ifeq ($(OS_ARCH),WINNT) _MOCHITEST_FILES += \ From 1b678d8fe12f2e2048df58b1e09141368040c00f Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Wed, 16 Dec 2009 14:11:51 -0600 Subject: [PATCH 08/14] Bug 533529 - Use credentials instead of context for sspi init check. r-bz. --- extensions/auth/nsAuthSSPI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/auth/nsAuthSSPI.cpp b/extensions/auth/nsAuthSSPI.cpp index 64cc1be3880..866b0b13b32 100644 --- a/extensions/auth/nsAuthSSPI.cpp +++ b/extensions/auth/nsAuthSSPI.cpp @@ -315,7 +315,7 @@ nsAuthSSPI::GetNextToken(const void *inToken, LOG(("entering nsAuthSSPI::GetNextToken()\n")); - if (!mCtxt.dwLower && !mCtxt.dwUpper) { + if (!mCred.dwLower && !mCred.dwUpper) { LOG(("nsAuthSSPI::GetNextToken(), not initialized. exiting.")); return NS_ERROR_NOT_INITIALIZED; } From 8949cc780f40d87e6d87aff481c334cea591c090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Wed, 16 Dec 2009 21:27:42 +0100 Subject: [PATCH 09/14] Bug 535104 - With the search bar removed from the toolbar, Ctrl+K still unhides the toolbar in full screen mode. r=rflint --- browser/base/content/browser.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index c5c62fa6ca2..c3e2b85ec65 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -3091,10 +3091,10 @@ const BrowserSearch = { return; } #endif - if (window.fullScreen) + var searchBar = this.searchBar; + if (searchBar && window.fullScreen) FullScreen.mouseoverToggle(true); - var searchBar = this.searchBar; if (isElementVisible(searchBar)) { searchBar.select(); searchBar.focus(); From c65df7f22e93408dd20e15511e86a22db339b013 Mon Sep 17 00:00:00 2001 From: Josh Aas Date: Wed, 16 Dec 2009 15:54:16 -0500 Subject: [PATCH 10/14] Fix plugin host so that idle event timer can QI correctly and make the callback. b=535357 r=smichaud --- modules/plugin/base/src/nsPluginHost.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/plugin/base/src/nsPluginHost.cpp b/modules/plugin/base/src/nsPluginHost.cpp index da3d7e77777..aa01ea3124c 100644 --- a/modules/plugin/base/src/nsPluginHost.cpp +++ b/modules/plugin/base/src/nsPluginHost.cpp @@ -1754,9 +1754,10 @@ nsPluginHost::~nsPluginHost() sInst = nsnull; } -NS_IMPL_ISUPPORTS3(nsPluginHost, +NS_IMPL_ISUPPORTS4(nsPluginHost, nsIPluginHost, nsIObserver, + nsITimerCallback, nsISupportsWeakReference) nsPluginHost* From 70eb109f81b1cce43594ae910d34e65e2a898a26 Mon Sep 17 00:00:00 2001 From: Benjamin Smedberg Date: Wed, 16 Dec 2009 16:14:22 -0500 Subject: [PATCH 11/14] Mark the two tests random on Windows which are causing problems, bug 524732 (reverting http://hg.mozilla.org/mozilla-central/rev/9f7c1fa9b2de to random-if instead of fails-if) --- js/src/tests/js1_5/Regress/jstests.list | 2 +- js/src/tests/js1_5/Scope/jstests.list | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/tests/js1_5/Regress/jstests.list b/js/src/tests/js1_5/Regress/jstests.list index 2892b1c6bb2..af07e2f2fcd 100644 --- a/js/src/tests/js1_5/Regress/jstests.list +++ b/js/src/tests/js1_5/Regress/jstests.list @@ -170,7 +170,7 @@ skip-if(xulRuntime.OS=="WINNT"&&isDebugBuild) script regress-341360.js # slow script regress-343713.js script regress-343966.js script regress-344711-n.js -script regress-344804.js # bug 524732 +random-if(!xulRuntime.shell&&xulRuntime.OS=="WINNT") script regress-344804.js # bug 524732 script regress-344959.js script regress-346237.js script regress-346801.js diff --git a/js/src/tests/js1_5/Scope/jstests.list b/js/src/tests/js1_5/Scope/jstests.list index f77bb3910ed..220ea43527a 100644 --- a/js/src/tests/js1_5/Scope/jstests.list +++ b/js/src/tests/js1_5/Scope/jstests.list @@ -1,6 +1,6 @@ url-prefix ../../jsreftest.html?test=js1_5/Scope/ script regress-154693.js -script regress-181834.js # bug 524732 +random-if(!xulRuntime.shell&&xulRuntime.OS=="WINNT") script regress-181834.js # bug 524732 script regress-184107.js script regress-185485.js script regress-191276.js From ee856bb4a6b8c49d7060852b524e66e5f9daaf76 Mon Sep 17 00:00:00 2001 From: Doug Turner Date: Wed, 16 Dec 2009 14:08:14 -0800 Subject: [PATCH 12/14] Bug 535338 - UpdateVisibility should call VisibilityFullyObscured when clip is zero. r=roc --- layout/generic/nsObjectFrame.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/layout/generic/nsObjectFrame.cpp b/layout/generic/nsObjectFrame.cpp index b615e971a50..0f0d4562781 100644 --- a/layout/generic/nsObjectFrame.cpp +++ b/layout/generic/nsObjectFrame.cpp @@ -536,7 +536,7 @@ private: PRBool SetupXShm(); void ReleaseXShm(); void NativeImageDraw(NPRect* invalidRect = nsnull); - PRBool UpdateVisibility(); + PRBool UpdateVisibility(PRBool aVisible); #endif }; @@ -5604,7 +5604,7 @@ void nsPluginInstanceOwner::SetPluginHost(nsIPluginHost* aHost) } #if defined(MOZ_PLATFORM_HILDON) && defined(MOZ_WIDGET_GTK2) -PRBool nsPluginInstanceOwner::UpdateVisibility() +PRBool nsPluginInstanceOwner::UpdateVisibility(PRBool aVisible) { if (!mInstance) return PR_TRUE; @@ -5614,7 +5614,7 @@ PRBool nsPluginInstanceOwner::UpdateVisibility() XVisibilityEvent& visibilityEvent = pluginEvent.xvisibility; visibilityEvent.type = VisibilityNotify; visibilityEvent.display = 0; - visibilityEvent.state = VisibilityUnobscured; + visibilityEvent.state = aVisible ? VisibilityUnobscured : VisibilityFullyObscured; mInstance->HandleEvent(&pluginEvent, &handled); mWidgetVisible = PR_TRUE; @@ -5789,7 +5789,7 @@ nsPluginInstanceOwner::SetAbsoluteScreenPosition(nsIDOMElement* element, mBlitParentElement = element; - UpdateVisibility(); + UpdateVisibility(!(width == 0 && height == 0)); if (!mInstance) return NS_OK; From e4bea0b2ba61b7b0545ee39dc501fb162be1d118 Mon Sep 17 00:00:00 2001 From: Benjamin Smedberg Date: Wed, 16 Dec 2009 17:27:18 -0500 Subject: [PATCH 13/14] Bug 518924 - Implement NPN_AsyncCallback r=cjones --- dom/plugins/PluginModuleChild.cpp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/dom/plugins/PluginModuleChild.cpp b/dom/plugins/PluginModuleChild.cpp index becaf9f5791..c65aab42ec9 100644 --- a/dom/plugins/PluginModuleChild.cpp +++ b/dom/plugins/PluginModuleChild.cpp @@ -1268,14 +1268,35 @@ _poppopupsenabledstate(NPP aNPP) return false; } +class AsyncCallRunnable : public nsRunnable +{ +public: + AsyncCallRunnable(PluginThreadCallback aFunc, void* aUserData) + : mFunc(aFunc) + , mData(aUserData) + { } + + NS_IMETHOD Run() { + mFunc(mData); + return NS_OK; + } + +private: + PluginThreadCallback mFunc; + void* mData; +}; + void NP_CALLBACK _pluginthreadasynccall(NPP aNPP, PluginThreadCallback aFunc, void* aUserData) { _MOZ_LOG(__FUNCTION__); - AssertPluginThread(); - NS_NOTYETIMPLEMENTED("Implement me!"); + if (!aFunc) + return; + + nsCOMPtr e(new AsyncCallRunnable(aFunc, aUserData)); + NS_DispatchToMainThread(e); } NPError NP_CALLBACK From 0acdc7d2d3365d62dd1d093d9f24a7ab182ba68e Mon Sep 17 00:00:00 2001 From: Armen Zambrano Gasparnian Date: Wed, 16 Dec 2009 18:34:28 -0500 Subject: [PATCH 14/14] Bug 531275 - add the platform parameter to generate l10n nightly snippets r=coop,nthomas --- toolkit/locales/l10n.mk | 1 + tools/update-packaging/generatesnippet.py | 22 ++++++++++------------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/toolkit/locales/l10n.mk b/toolkit/locales/l10n.mk index 2cc28f858a4..89f86a3fb65 100644 --- a/toolkit/locales/l10n.mk +++ b/toolkit/locales/l10n.mk @@ -211,5 +211,6 @@ generate-snippet-%: --application-ini-file=$(STAGEDIST)/application.ini \ --locale=$* \ --product=$(MOZ_PKG_APPNAME) \ + --platform=$(MOZ_PKG_PLATFORM) \ --download-base-URL=$(DOWNLOAD_BASE_URL) \ --verbose diff --git a/tools/update-packaging/generatesnippet.py b/tools/update-packaging/generatesnippet.py index 6c49d31e733..93475ff6af7 100644 --- a/tools/update-packaging/generatesnippet.py +++ b/tools/update-packaging/generatesnippet.py @@ -66,6 +66,10 @@ def main(): action="store", dest="product", help="[Required] This option is used to generate the URL to download the MAR file.") + parser.add_option("--platform", + action="store", + dest="platform", + help="[Required] This option is used to indicate which target platform.") parser.add_option("--download-base-URL", action="store", dest="downloadBaseURL", @@ -80,7 +84,8 @@ def main(): for req, msg in (('marPath', "the absolute path to the where the MAR file is"), ('applicationIniFile', "the absolute path to the application.ini file."), ('locale', "a locale."), - ('product', "specify a product.")): + ('product', "specify a product."), + ('platform', "specify the platform.")): if not hasattr(options, req): parser.error('You must specify %s' % msg) @@ -91,7 +96,8 @@ def main(): options.applicationIniFile, options.locale, options.downloadBaseURL, - options.product) + options.product, + options.platform) f = open(os.path.join(options.marPath, 'complete.update.snippet'), 'wb') f.write(snippet) f.close() @@ -101,7 +107,7 @@ def main(): print snippet def generateSnippet(abstDistDir, applicationIniFile, locale, - downloadBaseURL, product): + downloadBaseURL, product, platform): # Let's extract information from application.ini c = ConfigParser() try: @@ -116,7 +122,7 @@ def generateSnippet(abstDistDir, applicationIniFile, locale, product, appVersion, locale, - getPlatform()) + platform) # Let's determine the hash and the size of the MAR file # This function exits the script if the file does not exist (completeMarHash, completeMarSize) = getFileHashAndSize( @@ -148,14 +154,6 @@ sha1 return snippet -def getPlatform(): - if platform.system() == "Linux": - return "linux-i686" - elif platform.system() in ("Windows", "Microsoft"): - return "win32" - elif platform.system() == "Darwin": - return "mac" - def getFileHashAndSize(filepath): sha1Hash = 'UNKNOWN' size = 'UNKNOWN'