summaryrefslogtreecommitdiff
path: root/chromium/third_party/crashpad
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@theqtcompany.com>2015-06-18 14:10:49 +0200
committerOswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>2015-06-18 13:53:24 +0000
commit813fbf95af77a531c57a8c497345ad2c61d475b3 (patch)
tree821b2c8de8365f21b6c9ba17a236fb3006a1d506 /chromium/third_party/crashpad
parentaf6588f8d723931a298c995fa97259bb7f7deb55 (diff)
downloadqtwebengine-chromium-813fbf95af77a531c57a8c497345ad2c61d475b3.tar.gz
BASELINE: Update chromium to 44.0.2403.47
Change-Id: Ie056fedba95cf5e5c76b30c4b2c80fca4764aa2f Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
Diffstat (limited to 'chromium/third_party/crashpad')
-rw-r--r--chromium/third_party/crashpad/OWNERS3
-rw-r--r--chromium/third_party/crashpad/README.chromium14
-rw-r--r--chromium/third_party/crashpad/crashpad/.clang-format19
-rw-r--r--chromium/third_party/crashpad/crashpad/AUTHORS9
-rw-r--r--chromium/third_party/crashpad/crashpad/CONTRIBUTORS14
-rw-r--r--chromium/third_party/crashpad/crashpad/DEPS40
-rw-r--r--chromium/third_party/crashpad/crashpad/LICENSE202
-rw-r--r--chromium/third_party/crashpad/crashpad/build/crashpad.gypi23
-rw-r--r--chromium/third_party/crashpad/crashpad/build/crashpad_in_chromium.gypi43
-rwxr-xr-xchromium/third_party/crashpad/crashpad/build/gyp_crashpad.py46
-rwxr-xr-xchromium/third_party/crashpad/crashpad/build/run_tests.py62
-rw-r--r--chromium/third_party/crashpad/crashpad/client/capture_context_mac.S217
-rw-r--r--chromium/third_party/crashpad/crashpad/client/capture_context_mac.h48
-rw-r--r--chromium/third_party/crashpad/crashpad/client/capture_context_mac_test.cc148
-rw-r--r--chromium/third_party/crashpad/crashpad/client/client.gyp67
-rw-r--r--chromium/third_party/crashpad/crashpad/client/client_test.gyp44
-rw-r--r--chromium/third_party/crashpad/crashpad/client/crash_report_database.cc47
-rw-r--r--chromium/third_party/crashpad/crashpad/client/crash_report_database.h312
-rw-r--r--chromium/third_party/crashpad/crashpad/client/crash_report_database_mac.mm611
-rw-r--r--chromium/third_party/crashpad/crashpad/client/crash_report_database_test.cc531
-rw-r--r--chromium/third_party/crashpad/crashpad/client/crash_report_database_win.cc762
-rw-r--r--chromium/third_party/crashpad/crashpad/client/crashpad_client.h126
-rw-r--r--chromium/third_party/crashpad/crashpad/client/crashpad_client_mac.cc227
-rw-r--r--chromium/third_party/crashpad/crashpad/client/crashpad_client_win.cc91
-rw-r--r--chromium/third_party/crashpad/crashpad/client/crashpad_info.cc96
-rw-r--r--chromium/third_party/crashpad/crashpad/client/crashpad_info.h128
-rw-r--r--chromium/third_party/crashpad/crashpad/client/settings.cc263
-rw-r--r--chromium/third_party/crashpad/crashpad/client/settings.h165
-rw-r--r--chromium/third_party/crashpad/crashpad/client/settings_test.cc181
-rw-r--r--chromium/third_party/crashpad/crashpad/client/simple_string_dictionary.cc45
-rw-r--r--chromium/third_party/crashpad/crashpad/client/simple_string_dictionary.h274
-rw-r--r--chromium/third_party/crashpad/crashpad/client/simple_string_dictionary_test.cc298
-rw-r--r--chromium/third_party/crashpad/crashpad/client/simulate_crash.h24
-rw-r--r--chromium/third_party/crashpad/crashpad/client/simulate_crash_mac.cc241
-rw-r--r--chromium/third_party/crashpad/crashpad/client/simulate_crash_mac.h60
-rw-r--r--chromium/third_party/crashpad/crashpad/client/simulate_crash_mac_test.cc386
-rw-r--r--chromium/third_party/crashpad/crashpad/codereview.settings18
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/compat.gyp83
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/mac/AvailabilityMacros.h44
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/mac/kern/exc_resource.h62
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/mac/mach-o/getsect.cc107
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/mac/mach-o/getsect.h69
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/mac/mach-o/loader.h32
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/mac/mach/mach.h97
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/mac/sys/resource.h26
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/non_mac/mach/mach.h41
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/non_win/dbghelp.h853
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/non_win/minwinbase.h49
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/non_win/timezoneapi.h61
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/non_win/verrsrc.h184
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/non_win/windows.h17
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/non_win/winnt.h182
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/win/getopt.h20
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/win/sys/time.h23
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/win/sys/types.h31
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/win/time.cc40
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/win/time.h36
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/win/winnt.h32
-rw-r--r--chromium/third_party/crashpad/crashpad/crashpad.gyp42
-rw-r--r--chromium/third_party/crashpad/crashpad/doc/developing.ad213
-rw-r--r--chromium/third_party/crashpad/crashpad/doc/index.ad42
-rw-r--r--chromium/third_party/crashpad/crashpad/doc/status.ad42
-rw-r--r--chromium/third_party/crashpad/crashpad/doc/support/asciidoc.conf58
-rw-r--r--chromium/third_party/crashpad/crashpad/doc/support/asciidoc.css20
-rw-r--r--chromium/third_party/crashpad/crashpad/doc/support/crashpad.doxy2369
-rw-r--r--chromium/third_party/crashpad/crashpad/doc/support/crashpad.doxy.h24
-rwxr-xr-xchromium/third_party/crashpad/crashpad/doc/support/generate_asciidoc.sh129
-rwxr-xr-xchromium/third_party/crashpad/crashpad/doc/support/generate_doxygen.sh29
-rw-r--r--chromium/third_party/crashpad/crashpad/doc/support/man_footer.ad40
-rw-r--r--chromium/third_party/crashpad/crashpad/handler/handler.gyp75
-rw-r--r--chromium/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.cc261
-rw-r--r--chromium/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.h86
-rw-r--r--chromium/third_party/crashpad/crashpad/handler/mac/crash_report_upload_thread.cc366
-rw-r--r--chromium/third_party/crashpad/crashpad/handler/mac/crash_report_upload_thread.h152
-rw-r--r--chromium/third_party/crashpad/crashpad/handler/mac/crashpad_handler.ad108
-rw-r--r--chromium/third_party/crashpad/crashpad/handler/mac/exception_handler_server.cc236
-rw-r--r--chromium/third_party/crashpad/crashpad/handler/mac/exception_handler_server.h70
-rw-r--r--chromium/third_party/crashpad/crashpad/handler/mac/main.cc188
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump.gyp76
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_context.h344
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.cc215
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.h149
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer_test.cc157
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_crashpad_info_writer.cc147
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_crashpad_info_writer.h112
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_crashpad_info_writer_test.cc315
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_exception_writer.cc121
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_exception_writer.h124
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_exception_writer_test.cc281
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_extensions.cc24
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_extensions.h531
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc229
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_file_writer.h124
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_file_writer_test.cc445
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.cc260
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.h193
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer_test.cc363
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer.cc369
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer.h132
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer_test.cc723
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer.cc239
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer.h166
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer_test.cc471
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_module_writer.cc459
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_module_writer.h357
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_module_writer_test.cc763
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer.cc98
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer.h79
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer_test.cc102
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_simple_string_dictionary_writer.cc187
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_simple_string_dictionary_writer.h145
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_simple_string_dictionary_writer_test.cc287
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_stream_writer.cc49
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_stream_writer.h66
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_string_writer.cc138
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_string_writer.h183
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc268
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_system_info_writer.cc299
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_system_info_writer.h196
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_system_info_writer_test.cc481
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_test.gyp66
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_thread_id_map.cc67
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_thread_id_map.h52
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_thread_id_map_test.cc190
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer.cc231
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer.h214
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer_test.cc690
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_writable.cc269
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_writable.h279
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_writable_test.cc837
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_writer_util.cc61
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_writer_util.h90
-rw-r--r--chromium/third_party/crashpad/crashpad/package.h29
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/cpu_architecture.h39
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/cpu_context.cc100
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/cpu_context.h219
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/cpu_context_test.cc166
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options.cc44
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options.h64
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options_test.cc256
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options_test_module.cc47
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/exception_snapshot.h100
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac.cc440
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac.h116
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac_test.cc421
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/exception_snapshot_mac.cc253
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/exception_snapshot_mac.h93
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader.cc173
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader.h93
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader_test.cc344
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.cc723
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.h354
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader_test.cc644
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader.cc301
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader.h262
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader_test.cc188
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_symbol_table_reader.cc292
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_symbol_table_reader.h135
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.cc68
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.h68
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/module_snapshot_mac.cc174
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/module_snapshot_mac.h95
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/process_reader.cc700
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/process_reader.h239
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/process_reader_test.cc829
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/process_snapshot_mac.cc212
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/process_snapshot_mac.h153
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/process_types.cc244
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/process_types.h183
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/all.proctype26
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/crashpad_info.proctype39
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/crashreporterclient.proctype40
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/custom.cc95
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/dyld_images.proctype110
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/flavors.h24
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/internal.h36
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/loader.proctype140
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/nlist.proctype30
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/traits.h42
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/process_types_test.cc264
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/system_snapshot_mac.cc399
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/system_snapshot_mac.h101
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/system_snapshot_mac_test.cc176
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.cc115
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.h85
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot.h77
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_simple_string_dictionary_reader.cc86
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_simple_string_dictionary_reader.h42
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_list_reader.cc72
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_list_reader.h42
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_reader.cc50
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_reader.h40
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/minidump/module_snapshot_minidump.cc174
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/minidump/module_snapshot_minidump.h97
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.cc325
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.h102
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc338
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/module_snapshot.h158
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/process_snapshot.h167
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/snapshot.gyp113
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/snapshot_test.gyp105
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/system_snapshot.h270
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/thread_snapshot.h69
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/win/module_snapshot_win.cc136
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/win/module_snapshot_win.h96
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.cc182
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.h120
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/win/process_reader_win.cc90
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/win/process_reader_win.h84
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/win/process_reader_win_test.cc48
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.cc164
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.h128
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/win/system_snapshot_win.cc356
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/win/system_snapshot_win.h96
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/win/system_snapshot_win_test.cc156
-rw-r--r--chromium/third_party/crashpad/crashpad/test/test.gyp70
-rw-r--r--chromium/third_party/crashpad/crashpad/test/test_test.gyp52
-rw-r--r--chromium/third_party/crashpad/crashpad/third_party/apple_cctools/README.crashpad44
-rw-r--r--chromium/third_party/crashpad/crashpad/third_party/apple_cctools/apple_cctools.gyp34
-rw-r--r--chromium/third_party/crashpad/crashpad/third_party/apple_cctools/cctools/APPLE_LICENSE367
-rw-r--r--chromium/third_party/crashpad/crashpad/third_party/apple_cctools/cctools/include/mach-o/getsect.h76
-rw-r--r--chromium/third_party/crashpad/crashpad/third_party/apple_cctools/cctools/libmacho/getsecbyname.c133
-rw-r--r--chromium/third_party/crashpad/crashpad/third_party/apple_cf/APPLE_LICENSE335
-rw-r--r--chromium/third_party/crashpad/crashpad/third_party/apple_cf/CFStreamAbstract.h205
-rw-r--r--chromium/third_party/crashpad/crashpad/third_party/apple_cf/README.crashpad15
-rw-r--r--chromium/third_party/crashpad/crashpad/third_party/getopt/LICENSE5
-rw-r--r--chromium/third_party/crashpad/crashpad/third_party/getopt/README.crashpad14
-rw-r--r--chromium/third_party/crashpad/crashpad/third_party/getopt/getopt.c418
-rw-r--r--chromium/third_party/crashpad/crashpad/third_party/getopt/getopt.gyp35
-rw-r--r--chromium/third_party/crashpad/crashpad/third_party/getopt/getopt.h63
-rw-r--r--chromium/third_party/crashpad/crashpad/third_party/gmock/README.crashpad14
-rw-r--r--chromium/third_party/crashpad/crashpad/third_party/gmock/gmock.gyp220
-rw-r--r--chromium/third_party/crashpad/crashpad/third_party/gtest/README.crashpad14
-rw-r--r--chromium/third_party/crashpad/crashpad/third_party/gtest/gtest.gyp255
-rw-r--r--chromium/third_party/crashpad/crashpad/third_party/gyp/README.crashpad13
-rw-r--r--chromium/third_party/crashpad/crashpad/third_party/mini_chromium/README.crashpad17
-rw-r--r--chromium/third_party/crashpad/crashpad/third_party/mini_chromium/mini_chromium.gyp46
-rw-r--r--chromium/third_party/crashpad/crashpad/tools/crashpad_database_util.ad159
-rw-r--r--chromium/third_party/crashpad/crashpad/tools/crashpad_database_util.cc582
-rw-r--r--chromium/third_party/crashpad/crashpad/tools/generate_dump.ad96
-rw-r--r--chromium/third_party/crashpad/crashpad/tools/generate_dump.cc223
-rw-r--r--chromium/third_party/crashpad/crashpad/tools/mac/catch_exception_tool.ad117
-rw-r--r--chromium/third_party/crashpad/crashpad/tools/mac/catch_exception_tool.cc314
-rw-r--r--chromium/third_party/crashpad/crashpad/tools/mac/exception_port_tool.ad188
-rw-r--r--chromium/third_party/crashpad/crashpad/tools/mac/exception_port_tool.cc593
-rw-r--r--chromium/third_party/crashpad/crashpad/tools/mac/on_demand_service_tool.ad102
-rw-r--r--chromium/third_party/crashpad/crashpad/tools/mac/on_demand_service_tool.mm197
-rw-r--r--chromium/third_party/crashpad/crashpad/tools/mac/run_with_crashpad.ad112
-rw-r--r--chromium/third_party/crashpad/crashpad/tools/mac/run_with_crashpad.cc189
-rw-r--r--chromium/third_party/crashpad/crashpad/tools/mac/sectaskaccess_info.plist12
-rw-r--r--chromium/third_party/crashpad/crashpad/tools/tool_support.cc101
-rw-r--r--chromium/third_party/crashpad/crashpad/tools/tool_support.h82
-rw-r--r--chromium/third_party/crashpad/crashpad/tools/tools.gyp193
-rw-r--r--chromium/third_party/crashpad/crashpad/util/file/file_io.cc74
-rw-r--r--chromium/third_party/crashpad/crashpad/util/file/file_io.h284
-rw-r--r--chromium/third_party/crashpad/crashpad/util/file/file_io_posix.cc154
-rw-r--r--chromium/third_party/crashpad/crashpad/util/file/file_io_test.cc207
-rw-r--r--chromium/third_party/crashpad/crashpad/util/file/file_io_win.cc212
-rw-r--r--chromium/third_party/crashpad/crashpad/util/file/file_reader.cc100
-rw-r--r--chromium/third_party/crashpad/crashpad/util/file/file_reader.h147
-rw-r--r--chromium/third_party/crashpad/crashpad/util/file/file_seeker.cc37
-rw-r--r--chromium/third_party/crashpad/crashpad/util/file/file_seeker.h54
-rw-r--r--chromium/third_party/crashpad/crashpad/util/file/file_writer.cc178
-rw-r--r--chromium/third_party/crashpad/crashpad/util/file/file_writer.h173
-rw-r--r--chromium/third_party/crashpad/crashpad/util/file/string_file.cc171
-rw-r--r--chromium/third_party/crashpad/crashpad/util/file/string_file.h80
-rw-r--r--chromium/third_party/crashpad/crashpad/util/file/string_file_test.cc497
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mac/checked_mach_address_range.h38
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mac/checked_mach_address_range_test.cc256
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mac/launchd.h147
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mac/launchd.mm141
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mac/launchd_test.mm302
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mac/mac_util.cc282
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mac/mac_util.h73
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mac/mac_util_test.mm143
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mac/service_management.cc136
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mac/service_management.h81
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mac/service_management_test.mm163
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mac/xattr.cc148
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mac/xattr.h97
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mac/xattr_test.cc122
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/child_port.defs64
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/child_port_handshake.cc347
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/child_port_handshake.h227
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/child_port_handshake_test.cc188
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/child_port_server.cc122
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/child_port_server.h77
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/child_port_server_test.cc136
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/child_port_types.h26
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/composite_mach_message_server.cc86
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/composite_mach_message_server.h103
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/composite_mach_message_server_test.cc304
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/exc_client_variants.cc129
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/exc_client_variants.h85
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/exc_client_variants_test.cc295
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/exc_server_variants.cc790
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/exc_server_variants.h208
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/exc_server_variants_test.cc1301
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/exception_behaviors.cc39
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/exception_behaviors.h94
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/exception_behaviors_test.cc72
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/exception_ports.cc133
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/exception_ports.h187
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/exception_ports_test.cc616
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/exception_types.cc212
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/exception_types.h79
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/exception_types_test.cc140
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/mach_extensions.cc79
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/mach_extensions.h104
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/mach_extensions_test.cc65
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/mach_message.cc252
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/mach_message.h179
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/mach_message_server.cc280
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/mach_message_server.h179
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/mach_message_server_test.cc853
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/mach_message_test.cc153
-rwxr-xr-xchromium/third_party/crashpad/crashpad/util/mach/mig.py123
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/notify_server.cc245
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/notify_server.h193
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/notify_server_test.cc556
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/scoped_task_suspend.cc39
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/scoped_task_suspend.h45
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/scoped_task_suspend_test.cc85
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach.cc545
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach.h120
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach_test.cc1066
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/task_for_pid.cc167
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/task_for_pid.h59
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/task_memory.cc158
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/task_memory.h177
-rw-r--r--chromium/third_party/crashpad/crashpad/util/mach/task_memory_test.cc555
-rw-r--r--chromium/third_party/crashpad/crashpad/util/misc/clock.h52
-rw-r--r--chromium/third_party/crashpad/crashpad/util/misc/clock_mac.cc45
-rw-r--r--chromium/third_party/crashpad/crashpad/util/misc/clock_posix.cc51
-rw-r--r--chromium/third_party/crashpad/crashpad/util/misc/clock_test.cc98
-rw-r--r--chromium/third_party/crashpad/crashpad/util/misc/clock_win.cc48
-rw-r--r--chromium/third_party/crashpad/crashpad/util/misc/initialization_state.h100
-rw-r--r--chromium/third_party/crashpad/crashpad/util/misc/initialization_state_dcheck.cc41
-rw-r--r--chromium/third_party/crashpad/crashpad/util/misc/initialization_state_dcheck.h188
-rw-r--r--chromium/third_party/crashpad/crashpad/util/misc/initialization_state_dcheck_test.cc135
-rw-r--r--chromium/third_party/crashpad/crashpad/util/misc/initialization_state_test.cc58
-rw-r--r--chromium/third_party/crashpad/crashpad/util/misc/scoped_forbid_return.cc27
-rw-r--r--chromium/third_party/crashpad/crashpad/util/misc/scoped_forbid_return.h54
-rw-r--r--chromium/third_party/crashpad/crashpad/util/misc/scoped_forbid_return_test.cc68
-rw-r--r--chromium/third_party/crashpad/crashpad/util/misc/symbolic_constants_common.h132
-rw-r--r--chromium/third_party/crashpad/crashpad/util/misc/tri_state.h41
-rw-r--r--chromium/third_party/crashpad/crashpad/util/misc/uuid.cc150
-rw-r--r--chromium/third_party/crashpad/crashpad/util/misc/uuid.h114
-rw-r--r--chromium/third_party/crashpad/crashpad/util/misc/uuid_test.cc240
-rw-r--r--chromium/third_party/crashpad/crashpad/util/net/http_body.cc114
-rw-r--r--chromium/third_party/crashpad/crashpad/util/net/http_body.h129
-rw-r--r--chromium/third_party/crashpad/crashpad/util/net/http_body_test.cc217
-rw-r--r--chromium/third_party/crashpad/crashpad/util/net/http_body_test_util.cc48
-rw-r--r--chromium/third_party/crashpad/crashpad/util/net/http_body_test_util.h49
-rw-r--r--chromium/third_party/crashpad/crashpad/util/net/http_headers.cc23
-rw-r--r--chromium/third_party/crashpad/crashpad/util/net/http_headers.h34
-rw-r--r--chromium/third_party/crashpad/crashpad/util/net/http_multipart_builder.cc199
-rw-r--r--chromium/third_party/crashpad/crashpad/util/net/http_multipart_builder.h90
-rw-r--r--chromium/third_party/crashpad/crashpad/util/net/http_multipart_builder_test.cc293
-rw-r--r--chromium/third_party/crashpad/crashpad/util/net/http_transport.cc53
-rw-r--r--chromium/third_party/crashpad/crashpad/util/net/http_transport.h106
-rw-r--r--chromium/third_party/crashpad/crashpad/util/net/http_transport_mac.mm223
-rw-r--r--chromium/third_party/crashpad/crashpad/util/net/http_transport_test.cc283
-rwxr-xr-xchromium/third_party/crashpad/crashpad/util/net/http_transport_test_server.py150
-rw-r--r--chromium/third_party/crashpad/crashpad/util/net/http_transport_win.cc244
-rw-r--r--chromium/third_party/crashpad/crashpad/util/numeric/checked_address_range.cc120
-rw-r--r--chromium/third_party/crashpad/crashpad/util/numeric/checked_address_range.h144
-rw-r--r--chromium/third_party/crashpad/crashpad/util/numeric/checked_address_range_test.cc260
-rw-r--r--chromium/third_party/crashpad/crashpad/util/numeric/checked_range.h115
-rw-r--r--chromium/third_party/crashpad/crashpad/util/numeric/checked_range_test.cc251
-rw-r--r--chromium/third_party/crashpad/crashpad/util/numeric/in_range_cast.h44
-rw-r--r--chromium/third_party/crashpad/crashpad/util/numeric/in_range_cast_test.cc119
-rw-r--r--chromium/third_party/crashpad/crashpad/util/numeric/int128.h52
-rw-r--r--chromium/third_party/crashpad/crashpad/util/numeric/int128_test.cc44
-rw-r--r--chromium/third_party/crashpad/crashpad/util/numeric/safe_assignment.h44
-rw-r--r--chromium/third_party/crashpad/crashpad/util/posix/close_multiple.cc158
-rw-r--r--chromium/third_party/crashpad/crashpad/util/posix/close_multiple.h44
-rw-r--r--chromium/third_party/crashpad/crashpad/util/posix/close_stdio.cc52
-rw-r--r--chromium/third_party/crashpad/crashpad/util/posix/close_stdio.h42
-rw-r--r--chromium/third_party/crashpad/crashpad/util/posix/drop_privileges.cc91
-rw-r--r--chromium/third_party/crashpad/crashpad/util/posix/drop_privileges.h40
-rw-r--r--chromium/third_party/crashpad/crashpad/util/posix/process_info.h152
-rw-r--r--chromium/third_party/crashpad/crashpad/util/posix/process_info_mac.cc233
-rw-r--r--chromium/third_party/crashpad/crashpad/util/posix/process_info_test.cc148
-rw-r--r--chromium/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.cc166
-rw-r--r--chromium/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.h48
-rw-r--r--chromium/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix_test.cc256
-rw-r--r--chromium/third_party/crashpad/crashpad/util/stdlib/cxx.h70
-rw-r--r--chromium/third_party/crashpad/crashpad/util/stdlib/map_insert.h56
-rw-r--r--chromium/third_party/crashpad/crashpad/util/stdlib/map_insert_test.cc54
-rw-r--r--chromium/third_party/crashpad/crashpad/util/stdlib/objc.h39
-rw-r--r--chromium/third_party/crashpad/crashpad/util/stdlib/pointer_container.h57
-rw-r--r--chromium/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.cc156
-rw-r--r--chromium/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.h63
-rw-r--r--chromium/third_party/crashpad/crashpad/util/stdlib/string_number_conversion_test.cc224
-rw-r--r--chromium/third_party/crashpad/crashpad/util/stdlib/strlcpy.cc32
-rw-r--r--chromium/third_party/crashpad/crashpad/util/stdlib/strlcpy.h54
-rw-r--r--chromium/third_party/crashpad/crashpad/util/stdlib/strlcpy_test.cc95
-rw-r--r--chromium/third_party/crashpad/crashpad/util/stdlib/strnlen.cc47
-rw-r--r--chromium/third_party/crashpad/crashpad/util/stdlib/strnlen.h49
-rw-r--r--chromium/third_party/crashpad/crashpad/util/stdlib/strnlen_test.cc45
-rw-r--r--chromium/third_party/crashpad/crashpad/util/string/split_string.cc33
-rw-r--r--chromium/third_party/crashpad/crashpad/util/string/split_string.h41
-rw-r--r--chromium/third_party/crashpad/crashpad/util/string/split_string_test.cc63
-rw-r--r--chromium/third_party/crashpad/crashpad/util/synchronization/semaphore.h80
-rw-r--r--chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_mac.cc45
-rw-r--r--chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_posix.cc57
-rw-r--r--chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_test.cc131
-rw-r--r--chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_win.cc50
-rw-r--r--chromium/third_party/crashpad/crashpad/util/thread/thread_log_messages.cc99
-rw-r--r--chromium/third_party/crashpad/crashpad/util/thread/thread_log_messages.h48
-rw-r--r--chromium/third_party/crashpad/crashpad/util/thread/thread_log_messages_test.cc190
-rw-r--r--chromium/third_party/crashpad/crashpad/util/util.gyp227
-rw-r--r--chromium/third_party/crashpad/crashpad/util/util_test.gyp152
-rw-r--r--chromium/third_party/crashpad/crashpad/util/win/address_types.h32
-rw-r--r--chromium/third_party/crashpad/crashpad/util/win/checked_win_address_range.h36
-rw-r--r--chromium/third_party/crashpad/crashpad/util/win/process_info.cc339
-rw-r--r--chromium/third_party/crashpad/crashpad/util/win/process_info.h107
-rw-r--r--chromium/third_party/crashpad/crashpad/util/win/process_info_test.cc172
-rw-r--r--chromium/third_party/crashpad/crashpad/util/win/process_info_test_child.cc45
-rw-r--r--chromium/third_party/crashpad/crashpad/util/win/process_structs.h291
-rw-r--r--chromium/third_party/crashpad/crashpad/util/win/scoped_handle.cc32
-rw-r--r--chromium/third_party/crashpad/crashpad/util/win/scoped_handle.h49
-rw-r--r--chromium/third_party/crashpad/crashpad/util/win/time.cc65
-rw-r--r--chromium/third_party/crashpad/crashpad/util/win/time.h36
-rw-r--r--chromium/third_party/crashpad/crashpad/util/win/time_test.cc34
426 files changed, 73157 insertions, 0 deletions
diff --git a/chromium/third_party/crashpad/OWNERS b/chromium/third_party/crashpad/OWNERS
new file mode 100644
index 00000000000..1e002677a39
--- /dev/null
+++ b/chromium/third_party/crashpad/OWNERS
@@ -0,0 +1,3 @@
+mark@chromium.org
+rsesek@chromium.org
+scottmg@chromium.org
diff --git a/chromium/third_party/crashpad/README.chromium b/chromium/third_party/crashpad/README.chromium
new file mode 100644
index 00000000000..57b42dff8df
--- /dev/null
+++ b/chromium/third_party/crashpad/README.chromium
@@ -0,0 +1,14 @@
+Name: Crashpad
+Short Name: crashpad
+URL: https://crashpad.googlecode.com/
+Version: unknown
+Revision: see DEPS
+License: Apache 2.0
+License File: crashpad/LICENSE
+Security Critical: yes
+
+Description:
+Crashpad is a crash-reporting system. It's the successor to Breakpad.
+
+Local Modifications:
+None.
diff --git a/chromium/third_party/crashpad/crashpad/.clang-format b/chromium/third_party/crashpad/crashpad/.clang-format
new file mode 100644
index 00000000000..0be5323f5be
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/.clang-format
@@ -0,0 +1,19 @@
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ BasedOnStyle: Chromium,
+ AlignTrailingComments: false,
+ BinPackArguments: false,
+}
diff --git a/chromium/third_party/crashpad/crashpad/AUTHORS b/chromium/third_party/crashpad/crashpad/AUTHORS
new file mode 100644
index 00000000000..dd7ff268d4f
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/AUTHORS
@@ -0,0 +1,9 @@
+# This is the official list of Crashpad authors for copyright purposes.
+# This file is distinct from the CONTRIBUTORS files.
+# See the latter for an explanation.
+
+# Names should be added to this file as:
+# Name or Organization <email address>
+# The email address is not required for organizations.
+
+Google Inc.
diff --git a/chromium/third_party/crashpad/crashpad/CONTRIBUTORS b/chromium/third_party/crashpad/crashpad/CONTRIBUTORS
new file mode 100644
index 00000000000..2080cc456e6
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/CONTRIBUTORS
@@ -0,0 +1,14 @@
+# People who have agreed to one of the CLAs and can contribute patches.
+# The AUTHORS file lists the copyright holders; this file
+# lists people. For example, Google employees are listed here
+# but not in AUTHORS, because Google holds the copyright.
+#
+# https://developers.google.com/open-source/cla/individual
+# https://developers.google.com/open-source/cla/corporate
+#
+# Names should be added to this file as:
+# Name <email address>
+
+Mark Mentovai <mark@chromium.org>
+Robert Sesek <rsesek@chromium.org>
+Scott Graham <scottmg@chromium.org>
diff --git a/chromium/third_party/crashpad/crashpad/DEPS b/chromium/third_party/crashpad/crashpad/DEPS
new file mode 100644
index 00000000000..33d9fddc34d
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/DEPS
@@ -0,0 +1,40 @@
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+vars = {
+ 'chromium_git': 'https://chromium.googlesource.com',
+}
+
+deps = {
+ 'crashpad/third_party/gmock/gmock':
+ Var('chromium_git') + '/external/gmock@' +
+ '29763965ab52f24565299976b936d1265cb6a271', # svn r501
+ 'crashpad/third_party/gtest/gtest':
+ Var('chromium_git') + '/external/gtest@' +
+ '8245545b6dc9c4703e6496d1efd19e975ad2b038', # svn r700
+ 'crashpad/third_party/gyp/gyp':
+ Var('chromium_git') + '/external/gyp@' +
+ '32ca1cd8e010d013a606a752fb49a603a3598071', # svn r2015
+ 'crashpad/third_party/mini_chromium/mini_chromium':
+ Var('chromium_git') + '/chromium/mini_chromium@' +
+ '98264cf63f22c80ba4ea33405adcc0ddcb8ce0bf',
+}
+
+hooks = [
+ {
+ 'name': 'gyp',
+ 'pattern': '\.gypi?$',
+ 'action': ['python', 'crashpad/build/gyp_crashpad.py'],
+ },
+]
diff --git a/chromium/third_party/crashpad/crashpad/LICENSE b/chromium/third_party/crashpad/crashpad/LICENSE
new file mode 100644
index 00000000000..d6456956733
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/chromium/third_party/crashpad/crashpad/build/crashpad.gypi b/chromium/third_party/crashpad/crashpad/build/crashpad.gypi
new file mode 100644
index 00000000000..0dad63ca136
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/build/crashpad.gypi
@@ -0,0 +1,23 @@
+# Copyright 2015 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'variables': {
+ # When building as a part of Chromium, this variable sets up the build to
+ # treat Crashpad as Chromium code. This enables warnings at an appropriate
+ # level and applies Chromium’s build/filename_rules.gypi. In a standalone
+ # build, this variable has no effect.
+ 'chromium_code': 1,
+ },
+}
diff --git a/chromium/third_party/crashpad/crashpad/build/crashpad_in_chromium.gypi b/chromium/third_party/crashpad/crashpad/build/crashpad_in_chromium.gypi
new file mode 100644
index 00000000000..0f646377cab
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/build/crashpad_in_chromium.gypi
@@ -0,0 +1,43 @@
+# Copyright 2015 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ # Crashpad can build as a standalone project or as part of Chromium. When
+ # building as a standalone project, it uses mini_chromium to provide the base
+ # library, and uses its own copy of gtest in third_party. When building as
+ # part of Chromium, it uses Chromium’s base library and copy of gtest. In
+ # order for Crashpad’s .gyp files to reference the correct versions depending
+ # on whether building standalone or as a part of Chromium, include this .gypi
+ # file and reference the crashpad_in_chromium variable.
+
+ 'variables': {
+ 'variables': {
+ # When building as a standalone project, build/gyp_crashpad.py sets
+ # crashpad_standalone to 1, and this % assignment will not override it.
+ # The variable will not be set when building as part of Chromium, so in
+ # that case, this will define it with value 0.
+ 'crashpad_standalone%': 0,
+ },
+
+ 'conditions': [
+ ['crashpad_standalone!=0', {
+ 'crashpad_in_chromium': 0,
+ }, {
+ 'crashpad_in_chromium': 1,
+ }],
+ ],
+
+ 'crashpad_in_chromium': '<(crashpad_in_chromium)',
+ },
+}
diff --git a/chromium/third_party/crashpad/crashpad/build/gyp_crashpad.py b/chromium/third_party/crashpad/crashpad/build/gyp_crashpad.py
new file mode 100755
index 00000000000..8e342ffd715
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/build/gyp_crashpad.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import sys
+
+script_dir = os.path.dirname(__file__)
+crashpad_dir = os.path.dirname(script_dir) if script_dir is not '' else '..'
+sys.path.insert(
+ 0, os.path.join(crashpad_dir, 'third_party', 'gyp', 'gyp', 'pylib'))
+
+import gyp
+
+def main(args):
+ if 'GYP_GENERATORS' not in os.environ:
+ os.environ['GYP_GENERATORS'] = 'ninja'
+
+ crashpad_dir_or_dot = crashpad_dir if crashpad_dir is not '' else '.'
+
+ args.extend(['-D', 'crashpad_standalone=1'])
+ args.extend(['--include', os.path.join(crashpad_dir,
+ 'third_party',
+ 'mini_chromium',
+ 'mini_chromium',
+ 'build',
+ 'common.gypi')])
+ args.extend(['--depth', crashpad_dir_or_dot])
+ args.append(os.path.join(crashpad_dir, 'crashpad.gyp'))
+
+ return gyp.main(args)
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/chromium/third_party/crashpad/crashpad/build/run_tests.py b/chromium/third_party/crashpad/crashpad/build/run_tests.py
new file mode 100755
index 00000000000..1f4a089a32e
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/build/run_tests.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import subprocess
+import sys
+
+
+# This script is primarily used from the waterfall so that the list of tests
+# that are run is maintained in-tree, rather than in a separate infrastructure
+# location in the recipe.
+def main(args):
+ if len(args) != 1:
+ print >>sys.stderr, 'usage: run_tests.py {Debug|Release}'
+ return 1;
+
+ crashpad_dir = \
+ os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir)
+
+ # In a standalone Crashpad build, the out directory is in the Crashpad root.
+ out_dir = os.path.join(crashpad_dir, 'out')
+ if not os.path.exists(out_dir):
+ # In an in-Chromium build, the out directory is in the Chromium root, and
+ # the Crashpad root is in third_party/crashpad/crashpad relative to the
+ # Chromium root.
+ chromium_dir = os.path.join(crashpad_dir, os.pardir, os.pardir, os.pardir)
+ out_dir = os.path.join(chromium_dir, 'out')
+ if not os.path.exists(out_dir):
+ raise Exception('could not determine out_dir', crashpad_dir)
+
+ binary_dir = os.path.join(out_dir, args[0])
+
+ tests = [
+ 'crashpad_client_test',
+ 'crashpad_minidump_test',
+ 'crashpad_snapshot_test',
+ 'crashpad_test_test',
+ 'crashpad_util_test',
+ ]
+ for test in tests:
+ print '-' * 80
+ print test
+ print '-' * 80
+ subprocess.check_call(os.path.join(binary_dir, test))
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/chromium/third_party/crashpad/crashpad/client/capture_context_mac.S b/chromium/third_party/crashpad/crashpad/client/capture_context_mac.S
new file mode 100644
index 00000000000..2e845bcc8e4
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/capture_context_mac.S
@@ -0,0 +1,217 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#if defined(__i386__) || defined(__x86_64__)
+
+// namespace crashpad {
+// void CaptureContext(x86_thread_state_t* x86_thread_state);
+// } // namespace crashpad
+#define CAPTURECONTEXT_SYMBOL __ZN8crashpad14CaptureContextEP16x86_thread_state
+
+ .section __TEXT,__text,regular,pure_instructions
+ .private_extern CAPTURECONTEXT_SYMBOL
+ .globl CAPTURECONTEXT_SYMBOL
+ .align 4, 0x90
+CAPTURECONTEXT_SYMBOL:
+
+#if defined(__i386__)
+
+ .cfi_startproc
+
+ pushl %ebp
+ .cfi_def_cfa_offset 8
+ .cfi_offset %ebp, -8
+ movl %esp, %ebp
+ .cfi_def_cfa_register %ebp
+
+ // Note that 16-byte stack alignment is not maintained because this function
+ // does not call out to any other.
+
+ // pushfl first, because some instructions (but probably none used here)
+ // affect %eflags. %eflags will be in -4(%rbp).
+ pushfl
+
+ // Save the original value of %eax, and use %eax to hold the x86_thread_state*
+ // argument. The original value of %eax will be in -8(%rbp).
+ pushl %eax
+ movl 8(%ebp), %eax
+
+ // Initialize the header identifying the x86_thread_state_t structure as
+ // carrying an x86_thread_state32_t (flavor x86_THREAD_STATE32) of size
+ // x86_THREAD_STATE32_COUNT 32-bit values.
+ movl $1, (%eax) // x86_thread_state->tsh.flavor
+ movl $16, 4(%eax) // x86_thread_state->tsh.count
+
+ // General-purpose registers whose values haven’t changed can be captured
+ // directly.
+ movl %ebx, 12(%eax) // x86_thread_state->uts.ts32.__ebx
+ movl %ecx, 16(%eax) // x86_thread_state->uts.ts32.__ecx
+ movl %edx, 20(%eax) // x86_thread_state->uts.ts32.__edx
+ movl %edi, 24(%eax) // x86_thread_state->uts.ts32.__edi
+ movl %esi, 28(%eax) // x86_thread_state->uts.ts32.__esi
+
+ // Now that the original value of %edx has been saved, it can be repurposed to
+ // hold other registers’ values.
+
+ // The original %eax was saved on the stack above.
+ movl -8(%ebp), %edx
+ movl %edx, 8(%eax) // x86_thread_state->uts.ts32.__eax
+
+ // The original %ebp was saved on the stack in this function’s prologue.
+ movl (%ebp), %edx
+ movl %edx, 32(%eax) // x86_thread_state->uts.ts32.__ebp
+
+ // %esp was saved in %ebp in this function’s prologue, but the caller’s %esp
+ // is 8 more than this value: 4 for the original %ebp saved on the stack in
+ // this function’s prologue, and 4 for the return address saved on the stack
+ // by the call instruction that reached this function.
+ leal 8(%ebp), %edx
+ movl %edx, 36(%eax) // x86_thread_state->uts.ts32.__esp
+
+ // The original %eflags was saved on the stack above.
+ movl -4(%ebp), %edx
+ movl %edx, 44(%eax) // x86_thread_state->uts.ts32.__eflags
+
+ // %eip can’t be accessed directly, but the return address saved on the stack
+ // by the call instruction that reached this function can be used.
+ movl 4(%ebp), %edx
+ movl %edx, 48(%eax) // x86_thread_state->uts.ts32.__eip
+
+ // The segment registers are 16 bits wide, but x86_thread_state declares them
+ // as unsigned 32-bit values, so zero the top half.
+ xorl %edx, %edx
+ movw %ss, %dx
+ movl %edx, 40(%eax) // x86_thread_state->uts.ts32.__ss
+ movw %cs, %dx
+ movl %edx, 52(%eax) // x86_thread_state->uts.ts32.__cs
+ movw %ds, %dx
+ movl %edx, 56(%eax) // x86_thread_state->uts.ts32.__ds
+ movw %es, %dx
+ movl %edx, 60(%eax) // x86_thread_state->uts.ts32.__es
+ movw %fs, %dx
+ movl %edx, 64(%eax) // x86_thread_state->uts.ts32.__fs
+ movw %gs, %dx
+ movl %edx, 68(%eax) // x86_thread_state->uts.ts32.__gs
+
+ // Clean up by restoring clobbered registers, even those considered volatile
+ // by the ABI, so that the captured context represents the state at this
+ // function’s exit.
+ popl %eax
+ popfl
+
+ popl %ebp
+
+ ret
+
+ .cfi_endproc
+
+#elif defined(__x86_64__)
+
+ .cfi_startproc
+
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+
+ // Note that 16-byte stack alignment is not maintained because this function
+ // does not call out to any other.
+
+ // pushfq first, because some instructions (but probably none used here)
+ // affect %rflags. %rflags will be in -8(%rbp).
+ pushfq
+
+ // Initialize the header identifying the x86_thread_state_t structure as
+ // carrying an x86_thread_state64_t (flavor x86_THREAD_STATE64) of size
+ // x86_THREAD_STATE64_COUNT 32-bit values.
+ movl $4, (%rdi) // x86_thread_state->tsh.flavor
+ movl $42, 4(%rdi) // x86_thread_state->tsh.count
+
+ // General-purpose registers whose values haven’t changed can be captured
+ // directly.
+ movq %rax, 8(%rdi) // x86_thread_state->uts.ts64.__rax
+ movq %rbx, 16(%rdi) // x86_thread_state->uts.ts64.__rbx
+ movq %rcx, 24(%rdi) // x86_thread_state->uts.ts64.__rcx
+ movq %rdx, 32(%rdi) // x86_thread_state->uts.ts64.__rdx
+ movq %rsi, 48(%rdi) // x86_thread_state->uts.ts64.__rsi
+ movq %r8, 72(%rdi) // x86_thread_state->uts.ts64.__r8
+ movq %r9, 80(%rdi) // x86_thread_state->uts.ts64.__r9
+ movq %r10, 88(%rdi) // x86_thread_state->uts.ts64.__r10
+ movq %r11, 96(%rdi) // x86_thread_state->uts.ts64.__r11
+ movq %r12, 104(%rdi) // x86_thread_state->uts.ts64.__r12
+ movq %r13, 112(%rdi) // x86_thread_state->uts.ts64.__r13
+ movq %r14, 120(%rdi) // x86_thread_state->uts.ts64.__r14
+ movq %r15, 128(%rdi) // x86_thread_state->uts.ts64.__r15
+
+ // Because of the calling convention, there’s no way to recover the value of
+ // the caller’s %rdi as it existed prior to calling this function. This
+ // function captures a snapshot of the register state at its return, which
+ // involves %rdi containing a pointer to its first argument. Callers that
+ // require the value of %rdi prior to calling this function should obtain it
+ // separately. For example:
+ // uint64_t rdi;
+ // asm("movq %%rdi, %0" : "=m"(rdi));
+ movq %rdi, 40(%rdi) // x86_thread_state->uts.ts64.__rdi
+
+ // Now that the original value of %rax has been saved, it can be repurposed to
+ // hold other registers’ values.
+
+ // The original %rbp was saved on the stack in this function’s prologue.
+ movq (%rbp), %rax
+ movq %rax, 56(%rdi) // x86_thread_state->uts.ts64.__rbp
+
+ // %rsp was saved in %rbp in this function’s prologue, but the caller’s %rsp
+ // is 16 more than this value: 8 for the original %rbp saved on the stack in
+ // this function’s prologue, and 8 for the return address saved on the stack
+ // by the call instruction that reached this function.
+ leaq 16(%rbp), %rax
+ movq %rax, 64(%rdi) // x86_thread_state->uts.ts64.__rsp
+
+ // %rip can’t be accessed directly, but the return address saved on the stack
+ // by the call instruction that reached this function can be used.
+ movq 8(%rbp), %rax
+ movq %rax, 136(%rdi) // x86_thread_state->uts.ts64.__rip
+
+ // The original %rflags was saved on the stack above.
+ movq -8(%rbp), %rax
+ movq %rax, 144(%rdi) // x86_thread_state->uts.ts64.__rflags
+
+ // The segment registers are 16 bits wide, but x86_thread_state declares them
+ // as unsigned 64-bit values, so zero the top portion.
+ xorq %rax, %rax
+ movw %cs, %ax
+ movq %rax, 152(%rdi) // x86_thread_state->uts.ts64.__cs
+ movw %fs, %ax
+ movq %rax, 160(%rdi) // x86_thread_state->uts.ts64.__fs
+ movw %gs, %ax
+ movq %rax, 168(%rdi) // x86_thread_state->uts.ts64.__gs
+
+ // Clean up by restoring clobbered registers, even those considered volatile
+ // by the ABI, so that the captured context represents the state at this
+ // function’s exit.
+ movq 8(%rdi), %rax
+ popfq
+
+ popq %rbp
+
+ ret
+
+ .cfi_endproc
+
+#endif
+
+.subsections_via_symbols
+
+#endif
diff --git a/chromium/third_party/crashpad/crashpad/client/capture_context_mac.h b/chromium/third_party/crashpad/crashpad/client/capture_context_mac.h
new file mode 100644
index 00000000000..74e440edb19
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/capture_context_mac.h
@@ -0,0 +1,48 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_CLIENT_CAPTURE_CONTEXT_MAC_H_
+#define CRASHPAD_CLIENT_CAPTURE_CONTEXT_MAC_H_
+
+#include <mach/mach.h>
+
+#include "build/build_config.h"
+
+namespace crashpad {
+
+#if defined(ARCH_CPU_X86_FAMILY)
+using NativeCPUContext = x86_thread_state;
+#endif
+
+//! \brief Saves the CPU context.
+//!
+//! The CPU context will be captured as accurately and completely as possible,
+//! containing an atomic snapshot at the point of this function’s return. This
+//! function does not modify any registers.
+//!
+//! \param[out] cpu_context The structure to store the context in.
+//!
+//! \note On x86_64, the value for `%%rdi` will be populated with the address of
+//! this function’s argument, as mandated by the ABI. If the value of
+//! `%%rdi` prior to calling this function is needed, it must be obtained
+//! separately prior to calling this function. For example:
+//! \code
+//! uint64_t rdi;
+//! asm("movq %%rdi, %0" : "=m"(rdi));
+//! \endcode
+void CaptureContext(NativeCPUContext* cpu_context);
+
+} // namespace crashpad
+
+#endif // CRASHPAD_CLIENT_CAPTURE_CONTEXT_MAC_H_
diff --git a/chromium/third_party/crashpad/crashpad/client/capture_context_mac_test.cc b/chromium/third_party/crashpad/crashpad/client/capture_context_mac_test.cc
new file mode 100644
index 00000000000..660776afe4c
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/capture_context_mac_test.cc
@@ -0,0 +1,148 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "client/capture_context_mac.h"
+
+#include <mach/mach.h>
+#include <stdint.h>
+
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "build/build_config.h"
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+// If the context structure has fields that tell whether it’s valid, such as
+// magic numbers or size fields, sanity-checks those fields for validity with
+// fatal gtest assertions. For other fields, where it’s possible to reason about
+// their validity based solely on their contents, sanity-checks via nonfatal
+// gtest assertions.
+void SanityCheckContext(NativeCPUContext* context) {
+#if defined(ARCH_CPU_X86)
+ ASSERT_EQ(x86_THREAD_STATE32, context->tsh.flavor);
+ ASSERT_EQ(implicit_cast<int>(x86_THREAD_STATE32_COUNT), context->tsh.count);
+#elif defined(ARCH_CPU_X86_64)
+ ASSERT_EQ(x86_THREAD_STATE64, context->tsh.flavor);
+ ASSERT_EQ(implicit_cast<int>(x86_THREAD_STATE64_COUNT), context->tsh.count);
+#endif
+
+#if defined(ARCH_CPU_X86_FAMILY)
+ // The segment registers are only capable of storing 16-bit quantities, but
+ // the context structure provides native integer-width fields for them. Ensure
+ // that the high bits are all clear.
+ //
+ // Many bit positions in the flags register are reserved and will always read
+ // a known value. Most reservd bits are always 0, but bit 1 is always 1. Check
+ // that the reserved bits are all set to their expected values. Note that the
+ // set of reserved bits may be relaxed over time with newer CPUs, and that
+ // this test may need to be changed to reflect these developments. The current
+ // set of reserved bits are 1, 3, 5, 15, and 22 and higher. See Intel Software
+ // Developer’s Manual, Volume 1: Basic Architecture (253665-051), 3.4.3
+ // “EFLAGS Register”, and AMD Architecture Programmer’s Manual, Volume 2:
+ // System Programming (24593-3.24), 3.1.6 “RFLAGS Register”.
+#if defined(ARCH_CPU_X86)
+ EXPECT_EQ(0u, context->uts.ts32.__cs & ~0xffff);
+ EXPECT_EQ(0u, context->uts.ts32.__ds & ~0xffff);
+ EXPECT_EQ(0u, context->uts.ts32.__es & ~0xffff);
+ EXPECT_EQ(0u, context->uts.ts32.__fs & ~0xffff);
+ EXPECT_EQ(0u, context->uts.ts32.__gs & ~0xffff);
+ EXPECT_EQ(0u, context->uts.ts32.__ss & ~0xffff);
+ EXPECT_EQ(2u, context->uts.ts32.__eflags & 0xffc0802a);
+#elif defined(ARCH_CPU_X86_64)
+ EXPECT_EQ(0u, context->uts.ts64.__cs & ~UINT64_C(0xffff));
+ EXPECT_EQ(0u, context->uts.ts64.__fs & ~UINT64_C(0xffff));
+ EXPECT_EQ(0u, context->uts.ts64.__gs & ~UINT64_C(0xffff));
+ EXPECT_EQ(2u, context->uts.ts64.__rflags & UINT64_C(0xffffffffffc0802a));
+#endif
+#endif
+}
+
+// A CPU-independent function to return the program counter.
+uintptr_t ProgramCounterFromContext(NativeCPUContext* context) {
+#if defined(ARCH_CPU_X86)
+ return context->uts.ts32.__eip;
+#elif defined(ARCH_CPU_X86_64)
+ return context->uts.ts64.__rip;
+#endif
+}
+
+// A CPU-independent function to return the stack pointer.
+uintptr_t StackPointerFromContext(NativeCPUContext* context) {
+#if defined(ARCH_CPU_X86)
+ return context->uts.ts32.__esp;
+#elif defined(ARCH_CPU_X86_64)
+ return context->uts.ts64.__rsp;
+#endif
+}
+
+void TestCaptureContext() {
+ NativeCPUContext context_1;
+ CaptureContext(&context_1);
+
+ {
+ SCOPED_TRACE("context_1");
+ ASSERT_NO_FATAL_FAILURE(SanityCheckContext(&context_1));
+ }
+
+ // The program counter reference value is this function’s address. The
+ // captured program counter should be slightly greater than or equal to the
+ // reference program counter.
+ const uintptr_t kReferencePC =
+ reinterpret_cast<uintptr_t>(TestCaptureContext);
+ uintptr_t pc = ProgramCounterFromContext(&context_1);
+ EXPECT_LT(pc - kReferencePC, 64u);
+
+ // Declare sp and context_2 here because all local variables need to be
+ // declared before computing the stack pointer reference value, so that the
+ // reference value can be the lowest value possible.
+ uintptr_t sp;
+ NativeCPUContext context_2;
+
+ // The stack pointer reference value is the lowest address of a local variable
+ // in this function. The captured program counter will be slightly less than
+ // or equal to the reference stack pointer.
+ const uintptr_t kReferenceSP =
+ std::min(std::min(reinterpret_cast<uintptr_t>(&context_1),
+ reinterpret_cast<uintptr_t>(&context_2)),
+ std::min(reinterpret_cast<uintptr_t>(&pc),
+ reinterpret_cast<uintptr_t>(&sp)));
+ sp = StackPointerFromContext(&context_1);
+ EXPECT_LT(kReferenceSP - sp, 512u);
+
+ // Capture the context again, expecting that the stack pointer stays the same
+ // and the program counter increases. Strictly speaking, there’s no guarantee
+ // that these conditions will hold, although they do for known compilers even
+ // under typical optimization.
+ CaptureContext(&context_2);
+
+ {
+ SCOPED_TRACE("context_2");
+ ASSERT_NO_FATAL_FAILURE(SanityCheckContext(&context_2));
+ }
+
+ EXPECT_EQ(sp, StackPointerFromContext(&context_2));
+ EXPECT_GT(ProgramCounterFromContext(&context_2), pc);
+}
+
+TEST(CaptureContextMac, CaptureContext) {
+ ASSERT_NO_FATAL_FAILURE(TestCaptureContext());
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/client/client.gyp b/chromium/third_party/crashpad/crashpad/client/client.gyp
new file mode 100644
index 00000000000..a34d7a903fc
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/client.gyp
@@ -0,0 +1,67 @@
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'includes': [
+ '../build/crashpad.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'crashpad_client',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../compat/compat.gyp:crashpad_compat',
+ '../third_party/mini_chromium/mini_chromium.gyp:base',
+ '../util/util.gyp:crashpad_util',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'capture_context_mac.S',
+ 'capture_context_mac.h',
+ 'crash_report_database.cc',
+ 'crash_report_database.h',
+ 'crash_report_database_mac.mm',
+ 'crash_report_database_win.cc',
+ 'crashpad_client.h',
+ 'crashpad_client_mac.cc',
+ 'crashpad_client_win.cc',
+ 'crashpad_info.cc',
+ 'crashpad_info.h',
+ 'settings.cc',
+ 'settings.h',
+ 'simple_string_dictionary.cc',
+ 'simple_string_dictionary.h',
+ 'simulate_crash.h',
+ 'simulate_crash_mac.cc',
+ 'simulate_crash_mac.h',
+ ],
+ 'conditions': [
+ ['OS=="win"', {
+ 'link_settings': {
+ 'libraries': [
+ '-lrpcrt4.lib',
+ ],
+ },
+ }],
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ },
+ ],
+}
diff --git a/chromium/third_party/crashpad/crashpad/client/client_test.gyp b/chromium/third_party/crashpad/crashpad/client/client_test.gyp
new file mode 100644
index 00000000000..cc6893f9b38
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/client_test.gyp
@@ -0,0 +1,44 @@
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'includes': [
+ '../build/crashpad.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'crashpad_client_test',
+ 'type': 'executable',
+ 'dependencies': [
+ 'client.gyp:crashpad_client',
+ '../compat/compat.gyp:crashpad_compat',
+ '../test/test.gyp:crashpad_test',
+ '../third_party/gtest/gtest.gyp:gtest',
+ '../third_party/gtest/gtest.gyp:gtest_main',
+ '../third_party/mini_chromium/mini_chromium.gyp:base',
+ '../util/util.gyp:crashpad_util',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'capture_context_mac_test.cc',
+ 'crash_report_database_test.cc',
+ 'settings_test.cc',
+ 'simple_string_dictionary_test.cc',
+ 'simulate_crash_mac_test.cc',
+ ],
+ },
+ ],
+}
diff --git a/chromium/third_party/crashpad/crashpad/client/crash_report_database.cc b/chromium/third_party/crashpad/crashpad/client/crash_report_database.cc
new file mode 100644
index 00000000000..b758ee563d6
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/crash_report_database.cc
@@ -0,0 +1,47 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "client/crash_report_database.h"
+
+namespace crashpad {
+
+CrashReportDatabase::Report::Report()
+ : uuid(),
+ file_path(),
+ id(),
+ creation_time(0),
+ uploaded(false),
+ last_upload_attempt_time(0),
+ upload_attempts(0) {
+}
+
+CrashReportDatabase::CallErrorWritingCrashReport::CallErrorWritingCrashReport(
+ CrashReportDatabase* database,
+ NewReport* new_report)
+ : database_(database),
+ new_report_(new_report) {
+}
+
+CrashReportDatabase::CallErrorWritingCrashReport::
+ ~CallErrorWritingCrashReport() {
+ if (new_report_) {
+ database_->ErrorWritingCrashReport(new_report_);
+ }
+}
+
+void CrashReportDatabase::CallErrorWritingCrashReport::Disarm() {
+ new_report_ = nullptr;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/client/crash_report_database.h b/chromium/third_party/crashpad/crashpad/client/crash_report_database.h
new file mode 100644
index 00000000000..baf95adcd33
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/crash_report_database.h
@@ -0,0 +1,312 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_CLIENT_CRASH_REPORT_DATABASE_H_
+#define CRASHPAD_CLIENT_CRASH_REPORT_DATABASE_H_
+
+#include <time.h>
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "util/file/file_io.h"
+#include "util/misc/uuid.h"
+
+namespace crashpad {
+
+class Settings;
+
+//! \brief An interface for managing a collection of crash report files and
+//! metadata associated with the crash reports.
+//!
+//! All Report objects that are returned by this class are logically const.
+//! They are snapshots of the database at the time the query was run, and the
+//! data returned is liable to change after the query is executed.
+//!
+//! The lifecycle of a crash report has three stages:
+//!
+//! 1. New: A crash report is created with PrepareNewCrashReport(), the
+//! the client then writes the report, and then calls
+//! FinishedWritingCrashReport() to make the report Pending.
+//! 2. Pending: The report has been written but has not been locally
+//! processed.
+//! 3. Completed: The report has been locally processed, either by uploading
+//! it to a collection server and calling RecordUploadAttempt(), or by
+//! calling SkipReportUpload().
+class CrashReportDatabase {
+ public:
+ //! \brief A crash report record.
+ //!
+ //! This represents the metadata for a crash report, as well as the location
+ //! of the report itself. A CrashReportDatabase maintains at least this
+ //! information.
+ struct Report {
+ Report();
+
+ //! A unique identifier by which this report will always be known to the
+ //! database.
+ UUID uuid;
+
+ //! The current location of the crash report on the client’s filesystem.
+ //! The location of a crash report may change over time, so the UUID should
+ //! be used as the canonical identifier.
+ base::FilePath file_path;
+
+ //! An identifier issued to this crash report by a collection server.
+ std::string id;
+
+ //! The time at which the report was generated.
+ time_t creation_time;
+
+ //! Whether this crash report was successfully uploaded to a collection
+ //! server.
+ bool uploaded;
+
+ //! The last timestamp at which an attempt was made to submit this crash
+ //! report to a collection server. If this is zero, then the report has
+ //! never been uploaded. If #uploaded is true, then this timestamp is the
+ //! time at which the report was uploaded, and no other attempts to upload
+ //! this report will be made.
+ time_t last_upload_attempt_time;
+
+ //! The number of times an attempt was made to submit this report to
+ //! a collection server. If this is more than zero, then
+ //! #last_upload_attempt_time will be set to the timestamp of the most
+ //! recent attempt.
+ int upload_attempts;
+ };
+
+ //! \brief A crash report that is in the process of being written.
+ //!
+ //! An instance of this struct should be created via PrepareNewCrashReport()
+ //! and destroyed with FinishedWritingCrashReport().
+ struct NewReport {
+ //! The file handle to which the report should be written.
+ FileHandle handle;
+
+ //! A unique identifier by which this report will always be known to the
+ //! database.
+ UUID uuid;
+
+ //! The path to the crash report being written.
+ base::FilePath path;
+ };
+
+ //! \brief A scoper to cleanly handle the interface requirement imposed by
+ //! PrepareNewCrashReport().
+ //!
+ //! Calls ErrorWritingCrashReport() upon destruction unless disarmed by
+ //! calling Disarm(). Armed upon construction.
+ class CallErrorWritingCrashReport {
+ public:
+ //! \brief Arms the object to call ErrorWritingCrashReport() on \a database
+ //! with an argument of \a new_report on destruction.
+ CallErrorWritingCrashReport(CrashReportDatabase* database,
+ NewReport* new_report);
+
+ //! \brief Calls ErrorWritingCrashReport() if the object is armed.
+ ~CallErrorWritingCrashReport();
+
+ //! \brief Disarms the object so that CallErrorWritingCrashReport() will not
+ //! be called upon destruction.
+ void Disarm();
+
+ private:
+ CrashReportDatabase* database_; // weak
+ NewReport* new_report_; // weak
+
+ DISALLOW_COPY_AND_ASSIGN(CallErrorWritingCrashReport);
+ };
+
+ //! \brief The result code for operations performed on a database.
+ enum OperationStatus {
+ //! \brief No error occurred.
+ kNoError = 0,
+
+ //! \brief The report that was requested could not be located.
+ kReportNotFound,
+
+ //! \brief An error occured while performing a file operation on a crash
+ //! report.
+ //!
+ //! A database is responsible for managing both the metadata about a report
+ //! and the actual crash report itself. This error is returned when an
+ //! error occurred when managing the report file. Additional information
+ //! will be logged.
+ kFileSystemError,
+
+ //! \brief An error occured while recording metadata for a crash report or
+ //! database-wide settings.
+ //!
+ //! A database is responsible for managing both the metadata about a report
+ //! and the actual crash report itself. This error is returned when an
+ //! error occurred when managing the metadata about a crash report or
+ //! database-wide settings. Additional information will be logged.
+ kDatabaseError,
+
+ //! \brief The operation could not be completed because a concurrent
+ //! operation affecting the report is occurring.
+ kBusyError,
+ };
+
+ virtual ~CrashReportDatabase() {}
+
+ //! \brief Initializes a database of crash reports.
+ //!
+ //! \param[in] path A path to the database to be created or opened.
+ //!
+ //! \return A database object on success, `nullptr` on failure with an error
+ //! logged.
+ static scoped_ptr<CrashReportDatabase> Initialize(const base::FilePath& path);
+
+ //! \brief Returns the Settings object for this database.
+ //!
+ //! \return A weak pointer to the Settings object, which is owned by the
+ //! database.
+ virtual Settings* GetSettings() = 0;
+
+ //! \brief Creates a record of a new crash report.
+ //!
+ //! Callers can then write the crash report using the file handle provided.
+ //! The caller does not own the new crash report record or its file handle,
+ //! both of which must be explicitly disposed of by calling
+ //! FinishedWritingCrashReport() or ErrorWritingCrashReport().
+ //!
+ //! To arrange to call ErrorWritingCrashReport() during any early return, use
+ //! CallErrorWritingCrashReport.
+ //!
+ //! \param[out] report A NewReport object containing a file handle to which
+ //! the crash report data should be written. Only valid if this returns
+ //! #kNoError. The caller must not delete the NewReport object or close
+ //! the file handle within.
+ //!
+ //! \return The operation status code.
+ virtual OperationStatus PrepareNewCrashReport(NewReport** report) = 0;
+
+ //! \brief Informs the database that a crash report has been written.
+ //!
+ //! After calling this method, the database is permitted to move and rename
+ //! the file at NewReport::path.
+ //!
+ //! \param[in] report A NewReport obtained with PrepareNewCrashReport(). The
+ //! NewReport object and file handle within will be invalidated as part of
+ //! this call.
+ //! \param[out] uuid The UUID of this crash report.
+ //!
+ //! \return The operation status code.
+ virtual OperationStatus FinishedWritingCrashReport(NewReport* report,
+ UUID* uuid) = 0;
+
+ //! \brief Informs the database that an error occurred while attempting to
+ //! write a crash report, and that any resources associated with it should
+ //! be cleaned up.
+ //!
+ //! After calling this method, the database is permitted to remove the file at
+ //! NewReport::path.
+ //!
+ //! \param[in] report A NewReport obtained with PrepareNewCrashReport(). The
+ //! NewReport object and file handle within will be invalidated as part of
+ //! this call.
+ //!
+ //! \return The operation status code.
+ virtual OperationStatus ErrorWritingCrashReport(NewReport* report) = 0;
+
+ //! \brief Returns the crash report record for the unique identifier.
+ //!
+ //! \param[in] uuid The crash report record unique identifier.
+ //! \param[out] report A crash report record. Only valid if this returns
+ //! #kNoError.
+ //!
+ //! \return The operation status code.
+ virtual OperationStatus LookUpCrashReport(const UUID& uuid,
+ Report* report) = 0;
+
+ //! \brief Returns a list of crash report records that have not been uploaded.
+ //!
+ //! \param[out] reports A list of crash report record objects. This must be
+ //! empty on entry. Only valid if this returns #kNoError.
+ //!
+ //! \return The operation status code.
+ virtual OperationStatus GetPendingReports(std::vector<Report>* reports) = 0;
+
+ //! \brief Returns a list of crash report records that have been completed,
+ //! either by being uploaded or by skipping upload.
+ //!
+ //! \param[out] reports A list of crash report record objects. This must be
+ //! empty on entry. Only valid if this returns #kNoError.
+ //!
+ //! \return The operation status code.
+ virtual OperationStatus GetCompletedReports(std::vector<Report>* reports) = 0;
+
+ //! \brief Obtains a report object for uploading to a collection server.
+ //!
+ //! The file at Report::file_path should be uploaded by the caller, and then
+ //! the returned Report object must be disposed of via a call to
+ //! RecordUploadAttempt().
+ //!
+ //! A subsequent call to this method with the same \a uuid is illegal until
+ //! RecordUploadAttempt() has been called.
+ //!
+ //! \param[in] uuid The unique identifier for the crash report record.
+ //! \param[out] report A crash report record for the report to be uploaded.
+ //! The caller does not own this object. Only valid if this returns
+ //! #kNoError.
+ //!
+ //! \return The operation status code.
+ virtual OperationStatus GetReportForUploading(const UUID& uuid,
+ const Report** report) = 0;
+
+ //! \brief Adjusts a crash report record’s metadata to account for an upload
+ //! attempt, and updates the last upload attempt time as returned by
+ //! Settings::GetLastUploadAttemptTime().
+ //!
+ //! After calling this method, the database is permitted to move and rename
+ //! the file at Report::file_path.
+ //!
+ //! \param[in] report The report object obtained from
+ //! GetReportForUploading(). This object is invalidated after this call.
+ //! \param[in] successful Whether the upload attempt was successful.
+ //! \param[in] id The identifier assigned to this crash report by the
+ //! collection server. Must be empty if \a successful is `false`; may be
+ //! empty if it is `true`.
+ //!
+ //! \return The operation status code.
+ virtual OperationStatus RecordUploadAttempt(const Report* report,
+ bool successful,
+ const std::string& id) = 0;
+
+ //! \brief Moves a report from the pending state to the completed state, but
+ //! without the report being uploaded.
+ //!
+ //! This can be used if the user has disabled crash report collection, but
+ //! crash generation is still enabled in the product.
+ //!
+ //! \param[in] uuid The unique identifier for the crash report record.
+ //!
+ //! \return The operation status code.
+ virtual OperationStatus SkipReportUpload(const UUID& uuid) = 0;
+
+ protected:
+ CrashReportDatabase() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CrashReportDatabase);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_CLIENT_CRASH_REPORT_DATABASE_H_
diff --git a/chromium/third_party/crashpad/crashpad/client/crash_report_database_mac.mm b/chromium/third_party/crashpad/crashpad/client/crash_report_database_mac.mm
new file mode 100644
index 00000000000..f451efb0e0d
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/crash_report_database_mac.mm
@@ -0,0 +1,611 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "client/crash_report_database.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#import <Foundation/Foundation.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <uuid/uuid.h>
+
+#include "base/logging.h"
+#include "base/mac/scoped_nsautorelease_pool.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/scoped_generic.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/sys_string_conversions.h"
+#include "client/settings.h"
+#include "util/file/file_io.h"
+#include "util/mac/xattr.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+
+namespace {
+
+const char kWriteDirectory[] = "new";
+const char kUploadPendingDirectory[] = "pending";
+const char kCompletedDirectory[] = "completed";
+
+const char kSettings[] = "settings.dat";
+
+const char* const kReportDirectories[] = {
+ kWriteDirectory,
+ kUploadPendingDirectory,
+ kCompletedDirectory,
+};
+
+const char kCrashReportFileExtension[] = "dmp";
+
+const char kXattrUUID[] = "uuid";
+const char kXattrCollectorID[] = "id";
+const char kXattrCreationTime[] = "creation_time";
+const char kXattrIsUploaded[] = "uploaded";
+const char kXattrLastUploadTime[] = "last_upload_time";
+const char kXattrUploadAttemptCount[] = "upload_count";
+
+const char kXattrDatabaseInitialized[] = "initialized";
+
+// Ensures that the node at |path| is a directory, and creates it if it does
+// not exist. If the |path| points to a file, rather than a directory, or the
+// directory could not be created, returns false. Otherwise, returns true,
+// indicating that |path| already was or now is a directory.
+bool CreateOrEnsureDirectoryExists(const base::FilePath& path) {
+ if (mkdir(path.value().c_str(), 0755) == 0) {
+ return true;
+ } else if (errno == EEXIST) {
+ struct stat st;
+ if (stat(path.value().c_str(), &st) != 0) {
+ PLOG(ERROR) << "stat";
+ return false;
+ }
+ if (S_ISDIR(st.st_mode)) {
+ return true;
+ } else {
+ LOG(ERROR) << "not a directory";
+ return false;
+ }
+ } else {
+ PLOG(ERROR) << "mkdir";
+ return false;
+ }
+}
+
+//! \brief A CrashReportDatabase that uses HFS+ extended attributes to store
+//! report metadata.
+//!
+//! The database maintains three directories of reports: `"new"` to hold crash
+//! reports that are in the process of being written, `"completed"` to hold
+//! reports that have been written and are awaiting upload, and `"uploaded"` to
+//! hold reports successfully uploaded to a collection server. If the user has
+//! opted out of report collection, reports will still be written and moved
+//! to the completed directory, but they just will not be uploaded.
+//!
+//! The database stores its metadata in extended filesystem attributes. To
+//! ensure safe access, the report file is locked using `O_EXLOCK` during all
+//! extended attribute operations. The lock should be obtained using
+//! ObtainReportLock().
+class CrashReportDatabaseMac : public CrashReportDatabase {
+ public:
+ explicit CrashReportDatabaseMac(const base::FilePath& path);
+ virtual ~CrashReportDatabaseMac();
+
+ bool Initialize();
+
+ // CrashReportDatabase:
+ Settings* GetSettings() override;
+ OperationStatus PrepareNewCrashReport(NewReport** report) override;
+ OperationStatus FinishedWritingCrashReport(NewReport* report,
+ UUID* uuid) override;
+ OperationStatus ErrorWritingCrashReport(NewReport* report) override;
+ OperationStatus LookUpCrashReport(const UUID& uuid,
+ Report* report) override;
+ OperationStatus GetPendingReports(std::vector<Report>* reports) override;
+ OperationStatus GetCompletedReports(std::vector<Report>* reports) override;
+ OperationStatus GetReportForUploading(const UUID& uuid,
+ const Report** report) override;
+ OperationStatus RecordUploadAttempt(const Report* report,
+ bool successful,
+ const std::string& id) override;
+ OperationStatus SkipReportUpload(const UUID& uuid) override;
+
+ private:
+ //! \brief A private extension of the Report class that maintains bookkeeping
+ //! information of the database.
+ struct UploadReport : public Report {
+ //! \brief Stores the flock of the file for the duration of
+ //! GetReportForUploading() and RecordUploadAttempt().
+ int lock_fd;
+ };
+
+ //! \brief Locates a crash report in the database by UUID.
+ //!
+ //! \param[in] uuid The UUID of the crash report to locate.
+ //!
+ //! \return The full path to the report file, or an empty path if it cannot be
+ //! found.
+ base::FilePath LocateCrashReport(const UUID& uuid);
+
+ //! \brief Obtains an exclusive advisory lock on a file.
+ //!
+ //! The flock is used to prevent cross-process concurrent metadata reads or
+ //! writes. While xattrs do not observe the lock, if the lock-then-mutate
+ //! protocol is observed by all clients of the database, it still enforces
+ //! synchronization.
+ //!
+ //! This does not block, and so callers must ensure that the lock is valid
+ //! after calling.
+ //!
+ //! \param[in] path The path of the file to lock.
+ //!
+ //! \return A scoped lock object. If the result is not valid, an error is
+ //! logged.
+ static base::ScopedFD ObtainReportLock(const base::FilePath& path);
+
+ //! \brief Reads all the database xattrs from a file into a Report. The file
+ //! must be locked with ObtainReportLock.
+ //!
+ //! \param[in] path The path of the report.
+ //! \param[out] report The object into which data will be read.
+ //!
+ //! \return `true` if all the metadata was read successfully, `false`
+ //! otherwise.
+ static bool ReadReportMetadataLocked(const base::FilePath& path,
+ Report* report);
+
+ //! \brief Reads the metadata from all the reports in a database subdirectory.
+ //! Invalid reports are skipped.
+ //!
+ //! \param[in] path The database subdirectory path.
+ //! \param[out] reports An empty vector of reports, which will be filled.
+ //!
+ //! \return The operation status code.
+ static OperationStatus ReportsInDirectory(const base::FilePath& path,
+ std::vector<Report>* reports);
+
+
+ //! \brief Creates a database xattr name from the short constant name.
+ //!
+ //! \param[in] name The short name of the extended attribute.
+ //!
+ //! \return The long name of the extended attribute.
+ static std::string XattrName(const base::StringPiece& name);
+
+ base::FilePath base_dir_;
+ Settings settings_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrashReportDatabaseMac);
+};
+
+CrashReportDatabaseMac::CrashReportDatabaseMac(const base::FilePath& path)
+ : CrashReportDatabase(),
+ base_dir_(path),
+ settings_(base_dir_.Append(kSettings)),
+ initialized_() {
+}
+
+CrashReportDatabaseMac::~CrashReportDatabaseMac() {}
+
+bool CrashReportDatabaseMac::Initialize() {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ // Check if the database already exists.
+ if (!CreateOrEnsureDirectoryExists(base_dir_))
+ return false;
+
+ // Create the three processing directories for the database.
+ for (size_t i = 0; i < arraysize(kReportDirectories); ++i) {
+ if (!CreateOrEnsureDirectoryExists(base_dir_.Append(kReportDirectories[i])))
+ return false;
+ }
+
+ if (!settings_.Initialize())
+ return false;
+
+ // Write an xattr as the last step, to ensure the filesystem has support for
+ // them. This attribute will never be read.
+ if (!WriteXattrBool(base_dir_, XattrName(kXattrDatabaseInitialized), true))
+ return false;
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+Settings* CrashReportDatabaseMac::GetSettings() {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return &settings_;
+}
+
+CrashReportDatabase::OperationStatus
+CrashReportDatabaseMac::PrepareNewCrashReport(NewReport** out_report) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ scoped_ptr<NewReport> report(new NewReport());
+
+ uuid_t uuid_gen;
+ uuid_generate(uuid_gen);
+ report->uuid.InitializeFromBytes(uuid_gen);
+
+ report->path =
+ base_dir_.Append(kWriteDirectory)
+ .Append(report->uuid.ToString() + "." + kCrashReportFileExtension);
+
+ report->handle = HANDLE_EINTR(open(report->path.value().c_str(),
+ O_CREAT | O_WRONLY | O_EXCL | O_EXLOCK,
+ 0600));
+ if (report->handle < 0) {
+ PLOG(ERROR) << "open " << report->path.value();
+ return kFileSystemError;
+ }
+
+ // TODO(rsesek): Potentially use an fsetxattr() here instead.
+ if (!WriteXattr(
+ report->path, XattrName(kXattrUUID), report->uuid.ToString())) {
+ PLOG_IF(ERROR, IGNORE_EINTR(close(report->handle)) != 0) << "close";
+ return kDatabaseError;
+ }
+
+ *out_report = report.release();
+
+ return kNoError;
+}
+
+CrashReportDatabase::OperationStatus
+CrashReportDatabaseMac::FinishedWritingCrashReport(NewReport* report,
+ UUID* uuid) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ // Takes ownership of the |handle| and the O_EXLOCK.
+ base::ScopedFD lock(report->handle);
+
+ // Take ownership of the report.
+ scoped_ptr<NewReport> scoped_report(report);
+
+ // Get the report's UUID to return.
+ std::string uuid_string;
+ if (ReadXattr(report->path, XattrName(kXattrUUID),
+ &uuid_string) != XattrStatus::kOK ||
+ !uuid->InitializeFromString(uuid_string)) {
+ LOG(ERROR) << "Failed to read UUID for crash report "
+ << report->path.value();
+ return kDatabaseError;
+ }
+
+ if (*uuid != report->uuid) {
+ LOG(ERROR) << "UUID mismatch for crash report " << report->path.value();
+ return kDatabaseError;
+ }
+
+ // Record the creation time of this report.
+ if (!WriteXattrTimeT(report->path, XattrName(kXattrCreationTime),
+ time(nullptr))) {
+ return kDatabaseError;
+ }
+
+ // Move the report to its new location for uploading.
+ base::FilePath new_path =
+ base_dir_.Append(kUploadPendingDirectory).Append(report->path.BaseName());
+ if (rename(report->path.value().c_str(), new_path.value().c_str()) != 0) {
+ PLOG(ERROR) << "rename " << report->path.value() << " to "
+ << new_path.value();
+ return kFileSystemError;
+ }
+
+ return kNoError;
+}
+
+CrashReportDatabase::OperationStatus
+CrashReportDatabaseMac::ErrorWritingCrashReport(NewReport* report) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ // Takes ownership of the |handle| and the O_EXLOCK.
+ base::ScopedFD lock(report->handle);
+
+ // Take ownership of the report.
+ scoped_ptr<NewReport> scoped_report(report);
+
+ // Remove the file that the report would have been written to had no error
+ // occurred.
+ if (unlink(report->path.value().c_str()) != 0) {
+ PLOG(ERROR) << "unlink " << report->path.value();
+ return kFileSystemError;
+ }
+
+ return kNoError;
+}
+
+CrashReportDatabase::OperationStatus
+CrashReportDatabaseMac::LookUpCrashReport(const UUID& uuid,
+ CrashReportDatabase::Report* report) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ base::FilePath path = LocateCrashReport(uuid);
+ if (path.empty())
+ return kReportNotFound;
+
+ base::ScopedFD lock(ObtainReportLock(path));
+ if (!lock.is_valid())
+ return kBusyError;
+
+ *report = Report();
+ report->file_path = path;
+ if (!ReadReportMetadataLocked(path, report))
+ return kDatabaseError;
+
+ return kNoError;
+}
+
+CrashReportDatabase::OperationStatus
+CrashReportDatabaseMac::GetPendingReports(
+ std::vector<CrashReportDatabase::Report>* reports) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ return ReportsInDirectory(base_dir_.Append(kUploadPendingDirectory), reports);
+}
+
+CrashReportDatabase::OperationStatus
+CrashReportDatabaseMac::GetCompletedReports(
+ std::vector<CrashReportDatabase::Report>* reports) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ return ReportsInDirectory(base_dir_.Append(kCompletedDirectory), reports);
+}
+
+CrashReportDatabase::OperationStatus
+CrashReportDatabaseMac::GetReportForUploading(const UUID& uuid,
+ const Report** report) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ base::FilePath report_path = LocateCrashReport(uuid);
+ if (report_path.empty())
+ return kReportNotFound;
+
+ scoped_ptr<UploadReport> upload_report(new UploadReport());
+ upload_report->file_path = report_path;
+
+ base::ScopedFD lock(ObtainReportLock(report_path));
+ if (!lock.is_valid())
+ return kBusyError;
+
+ if (!ReadReportMetadataLocked(report_path, upload_report.get()))
+ return kDatabaseError;
+
+ upload_report->lock_fd = lock.release();
+ *report = upload_report.release();
+ return kNoError;
+}
+
+CrashReportDatabase::OperationStatus
+CrashReportDatabaseMac::RecordUploadAttempt(const Report* report,
+ bool successful,
+ const std::string& id) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ DCHECK(report);
+ DCHECK(successful || id.empty());
+
+ base::FilePath report_path = LocateCrashReport(report->uuid);
+ if (report_path.empty())
+ return kReportNotFound;
+
+ scoped_ptr<const UploadReport> upload_report(
+ static_cast<const UploadReport*>(report));
+
+ base::ScopedFD lock(upload_report->lock_fd);
+ if (!lock.is_valid())
+ return kBusyError;
+
+ if (successful) {
+ base::FilePath new_path =
+ base_dir_.Append(kCompletedDirectory).Append(report_path.BaseName());
+ if (rename(report_path.value().c_str(), new_path.value().c_str()) != 0) {
+ PLOG(ERROR) << "rename " << report_path.value() << " to "
+ << new_path.value();
+ return kFileSystemError;
+ }
+ report_path = new_path;
+ }
+
+ if (!WriteXattrBool(report_path, XattrName(kXattrIsUploaded), successful)) {
+ return kDatabaseError;
+ }
+ if (!WriteXattr(report_path, XattrName(kXattrCollectorID), id)) {
+ return kDatabaseError;
+ }
+
+ time_t now = time(nullptr);
+ if (!WriteXattrTimeT(report_path, XattrName(kXattrLastUploadTime), now)) {
+ return kDatabaseError;
+ }
+
+ int upload_attempts = 0;
+ std::string name = XattrName(kXattrUploadAttemptCount);
+ if (ReadXattrInt(report_path, name, &upload_attempts) ==
+ XattrStatus::kOtherError) {
+ return kDatabaseError;
+ }
+ if (!WriteXattrInt(report_path, name, ++upload_attempts)) {
+ return kDatabaseError;
+ }
+
+ if (!settings_.SetLastUploadAttemptTime(now)) {
+ return kDatabaseError;
+ }
+
+ return kNoError;
+}
+
+CrashReportDatabase::OperationStatus CrashReportDatabaseMac::SkipReportUpload(
+ const UUID& uuid) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ base::FilePath report_path = LocateCrashReport(uuid);
+ if (report_path.empty())
+ return kReportNotFound;
+
+ base::ScopedFD lock(ObtainReportLock(report_path));
+ if (!lock.is_valid())
+ return kBusyError;
+
+ base::FilePath new_path =
+ base_dir_.Append(kCompletedDirectory).Append(report_path.BaseName());
+ if (rename(report_path.value().c_str(), new_path.value().c_str()) != 0) {
+ PLOG(ERROR) << "rename " << report_path.value() << " to "
+ << new_path.value();
+ return kFileSystemError;
+ }
+
+ return kNoError;
+}
+
+base::FilePath CrashReportDatabaseMac::LocateCrashReport(const UUID& uuid) {
+ const std::string target_uuid = uuid.ToString();
+ for (size_t i = 0; i < arraysize(kReportDirectories); ++i) {
+ base::FilePath path =
+ base_dir_.Append(kReportDirectories[i])
+ .Append(target_uuid + "." + kCrashReportFileExtension);
+
+ // Test if the path exists.
+ struct stat st;
+ if (lstat(path.value().c_str(), &st)) {
+ continue;
+ }
+
+ // Check that the UUID of the report matches.
+ std::string uuid_string;
+ if (ReadXattr(path, XattrName(kXattrUUID),
+ &uuid_string) == XattrStatus::kOK &&
+ uuid_string == target_uuid) {
+ return path;
+ }
+ }
+
+ return base::FilePath();
+}
+
+// static
+base::ScopedFD CrashReportDatabaseMac::ObtainReportLock(
+ const base::FilePath& path) {
+ int fd = HANDLE_EINTR(open(path.value().c_str(),
+ O_RDONLY | O_EXLOCK | O_NONBLOCK));
+ PLOG_IF(ERROR, fd < 0) << "open lock " << path.value();
+ return base::ScopedFD(fd);
+}
+
+// static
+bool CrashReportDatabaseMac::ReadReportMetadataLocked(
+ const base::FilePath& path, Report* report) {
+ std::string uuid_string;
+ if (ReadXattr(path, XattrName(kXattrUUID),
+ &uuid_string) != XattrStatus::kOK ||
+ !report->uuid.InitializeFromString(uuid_string)) {
+ return false;
+ }
+
+ if (ReadXattrTimeT(path, XattrName(kXattrCreationTime),
+ &report->creation_time) != XattrStatus::kOK) {
+ return false;
+ }
+
+ report->id = std::string();
+ if (ReadXattr(path, XattrName(kXattrCollectorID),
+ &report->id) == XattrStatus::kOtherError) {
+ return false;
+ }
+
+ report->uploaded = false;
+ if (ReadXattrBool(path, XattrName(kXattrIsUploaded),
+ &report->uploaded) == XattrStatus::kOtherError) {
+ return false;
+ }
+
+ report->last_upload_attempt_time = 0;
+ if (ReadXattrTimeT(path, XattrName(kXattrLastUploadTime),
+ &report->last_upload_attempt_time) ==
+ XattrStatus::kOtherError) {
+ return false;
+ }
+
+ report->upload_attempts = 0;
+ if (ReadXattrInt(path, XattrName(kXattrUploadAttemptCount),
+ &report->upload_attempts) == XattrStatus::kOtherError) {
+ return false;
+ }
+
+ return true;
+}
+
+// static
+CrashReportDatabase::OperationStatus CrashReportDatabaseMac::ReportsInDirectory(
+ const base::FilePath& path,
+ std::vector<CrashReportDatabase::Report>* reports) {
+ base::mac::ScopedNSAutoreleasePool pool;
+
+ DCHECK(reports->empty());
+
+ NSError* error = nil;
+ NSArray* paths = [[NSFileManager defaultManager]
+ contentsOfDirectoryAtPath:base::SysUTF8ToNSString(path.value())
+ error:&error];
+ if (error) {
+ LOG(ERROR) << "Failed to enumerate reports in directory " << path.value()
+ << ": " << [[error description] UTF8String];
+ return kFileSystemError;
+ }
+
+ reports->reserve([paths count]);
+ for (NSString* entry in paths) {
+ Report report;
+ report.file_path = path.Append([entry fileSystemRepresentation]);
+ base::ScopedFD lock(ObtainReportLock(report.file_path));
+ if (!lock.is_valid())
+ continue;
+
+ if (!ReadReportMetadataLocked(report.file_path, &report)) {
+ LOG(WARNING) << "Failed to read report metadata for "
+ << report.file_path.value();
+ continue;
+ }
+ reports->push_back(report);
+ }
+
+ return kNoError;
+}
+
+// static
+std::string CrashReportDatabaseMac::XattrName(const base::StringPiece& name) {
+ return base::StringPrintf("com.googlecode.crashpad.%s", name.data());
+}
+
+} // namespace
+
+// static
+scoped_ptr<CrashReportDatabase> CrashReportDatabase::Initialize(
+ const base::FilePath& path) {
+ scoped_ptr<CrashReportDatabaseMac> database_mac(
+ new CrashReportDatabaseMac(path));
+ if (!database_mac->Initialize())
+ database_mac.reset();
+
+ return scoped_ptr<CrashReportDatabase>(database_mac.release());
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/client/crash_report_database_test.cc b/chromium/third_party/crashpad/crashpad/client/crash_report_database_test.cc
new file mode 100644
index 00000000000..6a04d19cfa8
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/crash_report_database_test.cc
@@ -0,0 +1,531 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "client/crash_report_database.h"
+
+#include <sys/stat.h>
+
+#include "build/build_config.h"
+#include "client/settings.h"
+#include "gtest/gtest.h"
+#include "test/errors.h"
+#include "test/scoped_temp_dir.h"
+#include "util/file/file_io.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+bool FileExistsAtPath(const base::FilePath& path) {
+#if defined(OS_POSIX)
+ struct stat st;
+ return lstat(path.value().c_str(), &st) == 0;
+#elif defined(OS_WIN)
+ struct _stat st;
+ return _wstat(path.value().c_str(), &st) == 0;
+#else
+#error "Not implemented"
+#endif
+}
+
+class CrashReportDatabaseTest : public testing::Test {
+ public:
+ CrashReportDatabaseTest() {
+ }
+
+ protected:
+ // testing::Test:
+ void SetUp() override {
+ db_ = CrashReportDatabase::Initialize(path());
+ ASSERT_TRUE(db_);
+ }
+
+ void ResetDatabase() {
+ db_.reset();
+ }
+
+ CrashReportDatabase* db() { return db_.get(); }
+ base::FilePath path() const {
+ return temp_dir_.path().Append(FILE_PATH_LITERAL("crashpad_test_database"));
+ }
+
+ void CreateCrashReport(CrashReportDatabase::Report* report) {
+ CrashReportDatabase::NewReport* new_report = nullptr;
+ ASSERT_EQ(CrashReportDatabase::kNoError,
+ db_->PrepareNewCrashReport(&new_report));
+ const char kTest[] = "test";
+ ASSERT_TRUE(LoggingWriteFile(new_report->handle, kTest, sizeof(kTest)));
+
+ UUID uuid;
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db_->FinishedWritingCrashReport(new_report, &uuid));
+
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db_->LookUpCrashReport(uuid, report));
+ ExpectPreparedCrashReport(*report);
+ ASSERT_TRUE(FileExistsAtPath(report->file_path));
+ }
+
+ void UploadReport(const UUID& uuid, bool successful, const std::string& id) {
+#if !defined(OS_WIN)
+ // Enable when ported to Windows.
+ // https://code.google.com/p/crashpad/issues/detail?id=13
+ Settings* const settings = db_->GetSettings();
+ ASSERT_TRUE(settings);
+ time_t times[2];
+ ASSERT_TRUE(settings->GetLastUploadAttemptTime(&times[0]));
+#endif
+
+ const CrashReportDatabase::Report* report = nullptr;
+ ASSERT_EQ(CrashReportDatabase::kNoError,
+ db_->GetReportForUploading(uuid, &report));
+ EXPECT_NE(UUID(), report->uuid);
+ EXPECT_FALSE(report->file_path.empty());
+ EXPECT_TRUE(FileExistsAtPath(report->file_path))
+ << report->file_path.value();
+ EXPECT_GT(report->creation_time, 0);
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db_->RecordUploadAttempt(report, successful, id));
+
+#if !defined(OS_WIN)
+ // Enable when ported to Windows.
+ // https://code.google.com/p/crashpad/issues/detail?id=13
+ ASSERT_TRUE(settings->GetLastUploadAttemptTime(&times[1]));
+ EXPECT_NE(times[1], 0);
+ EXPECT_GE(times[1], times[0]);
+#endif
+ }
+
+ void ExpectPreparedCrashReport(const CrashReportDatabase::Report& report) {
+ EXPECT_NE(UUID(), report.uuid);
+ EXPECT_FALSE(report.file_path.empty());
+ EXPECT_TRUE(FileExistsAtPath(report.file_path)) << report.file_path.value();
+ EXPECT_TRUE(report.id.empty());
+ EXPECT_GT(report.creation_time, 0);
+ EXPECT_FALSE(report.uploaded);
+ EXPECT_EQ(0, report.last_upload_attempt_time);
+ EXPECT_EQ(0, report.upload_attempts);
+ }
+
+ void RelocateDatabase() {
+ ResetDatabase();
+ temp_dir_.Rename();
+ SetUp();
+ }
+
+ private:
+ ScopedTempDir temp_dir_;
+ scoped_ptr<CrashReportDatabase> db_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrashReportDatabaseTest);
+};
+
+TEST_F(CrashReportDatabaseTest, Initialize) {
+ // Initialize the database for the first time, creating it.
+ ASSERT_TRUE(db());
+
+#if !defined(OS_WIN)
+ // Enable when ported to Windows.
+ // https://code.google.com/p/crashpad/issues/detail?id=13
+ Settings* settings = db()->GetSettings();
+
+ UUID client_ids[2];
+ ASSERT_TRUE(settings->GetClientID(&client_ids[0]));
+ EXPECT_NE(client_ids[0], UUID());
+
+ time_t last_upload_attempt_time;
+ ASSERT_TRUE(settings->GetLastUploadAttemptTime(&last_upload_attempt_time));
+ EXPECT_EQ(0, last_upload_attempt_time);
+#endif
+
+ // Close and reopen the database at the same path.
+ ResetDatabase();
+ EXPECT_FALSE(db());
+ auto db = CrashReportDatabase::Initialize(path());
+ ASSERT_TRUE(db);
+
+#if !defined(OS_WIN)
+ // Enable when ported to Windows.
+ // https://code.google.com/p/crashpad/issues/detail?id=13
+ settings = db->GetSettings();
+
+ ASSERT_TRUE(settings->GetClientID(&client_ids[1]));
+ EXPECT_EQ(client_ids[0], client_ids[1]);
+
+ ASSERT_TRUE(settings->GetLastUploadAttemptTime(&last_upload_attempt_time));
+ EXPECT_EQ(0, last_upload_attempt_time);
+#endif
+
+ std::vector<CrashReportDatabase::Report> reports;
+ EXPECT_EQ(CrashReportDatabase::kNoError, db->GetPendingReports(&reports));
+ EXPECT_TRUE(reports.empty());
+ reports.clear();
+ EXPECT_EQ(CrashReportDatabase::kNoError, db->GetCompletedReports(&reports));
+ EXPECT_TRUE(reports.empty());
+}
+
+TEST_F(CrashReportDatabaseTest, NewCrashReport) {
+ CrashReportDatabase::NewReport* new_report;
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->PrepareNewCrashReport(&new_report));
+ UUID expect_uuid = new_report->uuid;
+ EXPECT_TRUE(FileExistsAtPath(new_report->path)) << new_report->path.value();
+ UUID uuid;
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->FinishedWritingCrashReport(new_report, &uuid));
+ EXPECT_EQ(expect_uuid, uuid);
+
+ CrashReportDatabase::Report report;
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->LookUpCrashReport(uuid, &report));
+ ExpectPreparedCrashReport(report);
+
+ std::vector<CrashReportDatabase::Report> reports;
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->GetPendingReports(&reports));
+ ASSERT_EQ(1u, reports.size());
+ EXPECT_EQ(report.uuid, reports[0].uuid);
+
+ reports.clear();
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->GetCompletedReports(&reports));
+ EXPECT_TRUE(reports.empty());
+}
+
+TEST_F(CrashReportDatabaseTest, ErrorWritingCrashReport) {
+ CrashReportDatabase::NewReport* new_report = nullptr;
+ ASSERT_EQ(CrashReportDatabase::kNoError,
+ db()->PrepareNewCrashReport(&new_report));
+ base::FilePath new_report_path = new_report->path;
+ EXPECT_TRUE(FileExistsAtPath(new_report_path)) << new_report_path.value();
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->ErrorWritingCrashReport(new_report));
+ EXPECT_FALSE(FileExistsAtPath(new_report_path)) << new_report_path.value();
+}
+
+TEST_F(CrashReportDatabaseTest, LookUpCrashReport) {
+ UUID uuid;
+
+ {
+ CrashReportDatabase::Report report;
+ CreateCrashReport(&report);
+ uuid = report.uuid;
+ }
+
+ {
+ CrashReportDatabase::Report report;
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->LookUpCrashReport(uuid, &report));
+ EXPECT_EQ(uuid, report.uuid);
+ EXPECT_NE(std::string::npos, report.file_path.value().find(path().value()));
+ EXPECT_EQ(std::string(), report.id);
+ EXPECT_FALSE(report.uploaded);
+ EXPECT_EQ(0, report.last_upload_attempt_time);
+ EXPECT_EQ(0, report.upload_attempts);
+ }
+
+ UploadReport(uuid, true, "test");
+
+ {
+ CrashReportDatabase::Report report;
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->LookUpCrashReport(uuid, &report));
+ EXPECT_EQ(uuid, report.uuid);
+ EXPECT_NE(std::string::npos, report.file_path.value().find(path().value()));
+ EXPECT_EQ("test", report.id);
+ EXPECT_TRUE(report.uploaded);
+ EXPECT_NE(0, report.last_upload_attempt_time);
+ EXPECT_EQ(1, report.upload_attempts);
+ }
+}
+
+TEST_F(CrashReportDatabaseTest, RecordUploadAttempt) {
+ std::vector<CrashReportDatabase::Report> reports(3);
+ CreateCrashReport(&reports[0]);
+ CreateCrashReport(&reports[1]);
+ CreateCrashReport(&reports[2]);
+
+ // Record two attempts: one successful, one not.
+ UploadReport(reports[1].uuid, false, std::string());
+ UploadReport(reports[2].uuid, true, "abc123");
+
+ std::vector<CrashReportDatabase::Report> query(3);
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->LookUpCrashReport(reports[0].uuid, &query[0]));
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->LookUpCrashReport(reports[1].uuid, &query[1]));
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->LookUpCrashReport(reports[2].uuid, &query[2]));
+
+ EXPECT_EQ(std::string(), query[0].id);
+ EXPECT_EQ(std::string(), query[1].id);
+ EXPECT_EQ("abc123", query[2].id);
+
+ EXPECT_FALSE(query[0].uploaded);
+ EXPECT_FALSE(query[1].uploaded);
+ EXPECT_TRUE(query[2].uploaded);
+
+ EXPECT_EQ(0, query[0].last_upload_attempt_time);
+ EXPECT_NE(0, query[1].last_upload_attempt_time);
+ EXPECT_NE(0, query[2].last_upload_attempt_time);
+
+ EXPECT_EQ(0, query[0].upload_attempts);
+ EXPECT_EQ(1, query[1].upload_attempts);
+ EXPECT_EQ(1, query[2].upload_attempts);
+
+ // Attempt to upload and fail again.
+ UploadReport(reports[1].uuid, false, std::string());
+
+ time_t report_2_upload_time = query[2].last_upload_attempt_time;
+
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->LookUpCrashReport(reports[0].uuid, &query[0]));
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->LookUpCrashReport(reports[1].uuid, &query[1]));
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->LookUpCrashReport(reports[2].uuid, &query[2]));
+
+ EXPECT_FALSE(query[0].uploaded);
+ EXPECT_FALSE(query[1].uploaded);
+ EXPECT_TRUE(query[2].uploaded);
+
+ EXPECT_EQ(0, query[0].last_upload_attempt_time);
+ EXPECT_GE(query[1].last_upload_attempt_time, report_2_upload_time);
+ EXPECT_EQ(report_2_upload_time, query[2].last_upload_attempt_time);
+
+ EXPECT_EQ(0, query[0].upload_attempts);
+ EXPECT_EQ(2, query[1].upload_attempts);
+ EXPECT_EQ(1, query[2].upload_attempts);
+
+ // Third time's the charm: upload and succeed.
+ UploadReport(reports[1].uuid, true, "666hahaha");
+
+ time_t report_1_upload_time = query[1].last_upload_attempt_time;
+
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->LookUpCrashReport(reports[0].uuid, &query[0]));
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->LookUpCrashReport(reports[1].uuid, &query[1]));
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->LookUpCrashReport(reports[2].uuid, &query[2]));
+
+ EXPECT_FALSE(query[0].uploaded);
+ EXPECT_TRUE(query[1].uploaded);
+ EXPECT_TRUE(query[2].uploaded);
+
+ EXPECT_EQ(0, query[0].last_upload_attempt_time);
+ EXPECT_GE(query[1].last_upload_attempt_time, report_1_upload_time);
+ EXPECT_EQ(report_2_upload_time, query[2].last_upload_attempt_time);
+
+ EXPECT_EQ(0, query[0].upload_attempts);
+ EXPECT_EQ(3, query[1].upload_attempts);
+ EXPECT_EQ(1, query[2].upload_attempts);
+}
+
+// This test covers both query functions since they are related.
+TEST_F(CrashReportDatabaseTest, GetCompletedAndNotUploadedReports) {
+ std::vector<CrashReportDatabase::Report> reports(5);
+ CreateCrashReport(&reports[0]);
+ CreateCrashReport(&reports[1]);
+ CreateCrashReport(&reports[2]);
+ CreateCrashReport(&reports[3]);
+ CreateCrashReport(&reports[4]);
+
+ const UUID& report_0_uuid = reports[0].uuid;
+ const UUID& report_1_uuid = reports[1].uuid;
+ const UUID& report_2_uuid = reports[2].uuid;
+ const UUID& report_3_uuid = reports[3].uuid;
+ const UUID& report_4_uuid = reports[4].uuid;
+
+ std::vector<CrashReportDatabase::Report> pending;
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->GetPendingReports(&pending));
+
+ std::vector<CrashReportDatabase::Report> completed;
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->GetCompletedReports(&completed));
+
+ EXPECT_EQ(reports.size(), pending.size());
+ EXPECT_EQ(0u, completed.size());
+
+ // Upload one report successfully.
+ UploadReport(report_1_uuid, true, "report1");
+
+ pending.clear();
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->GetPendingReports(&pending));
+ completed.clear();
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->GetCompletedReports(&completed));
+
+ EXPECT_EQ(4u, pending.size());
+ ASSERT_EQ(1u, completed.size());
+
+ for (const auto& report : pending) {
+ EXPECT_NE(report_1_uuid, report.uuid);
+ EXPECT_FALSE(report.file_path.empty());
+ }
+ EXPECT_EQ(report_1_uuid, completed[0].uuid);
+ EXPECT_EQ("report1", completed[0].id);
+ EXPECT_EQ(true, completed[0].uploaded);
+ EXPECT_GT(completed[0].last_upload_attempt_time, 0);
+ EXPECT_EQ(1, completed[0].upload_attempts);
+
+ // Fail to upload one report.
+ UploadReport(report_2_uuid, false, std::string());
+
+ pending.clear();
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->GetPendingReports(&pending));
+ completed.clear();
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->GetCompletedReports(&completed));
+
+ EXPECT_EQ(4u, pending.size());
+ ASSERT_EQ(1u, completed.size());
+
+ for (const auto& report : pending) {
+ if (report.upload_attempts != 0) {
+ EXPECT_EQ(report_2_uuid, report.uuid);
+ EXPECT_GT(report.last_upload_attempt_time, 0);
+ EXPECT_FALSE(report.uploaded);
+ EXPECT_TRUE(report.id.empty());
+ }
+ EXPECT_FALSE(report.file_path.empty());
+ }
+
+ // Upload a second report.
+ UploadReport(report_4_uuid, true, "report_4");
+
+ pending.clear();
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->GetPendingReports(&pending));
+ completed.clear();
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->GetCompletedReports(&completed));
+
+ EXPECT_EQ(3u, pending.size());
+ ASSERT_EQ(2u, completed.size());
+
+ // Succeed the failed report.
+ UploadReport(report_2_uuid, true, "report 2");
+
+ pending.clear();
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->GetPendingReports(&pending));
+ completed.clear();
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->GetCompletedReports(&completed));
+
+ EXPECT_EQ(2u, pending.size());
+ ASSERT_EQ(3u, completed.size());
+
+ for (const auto& report : pending) {
+ EXPECT_TRUE(report.uuid == report_0_uuid ||
+ report.uuid == report_3_uuid);
+ EXPECT_FALSE(report.file_path.empty());
+ }
+
+ // Skip upload for one report.
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->SkipReportUpload(report_3_uuid));
+
+ pending.clear();
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->GetPendingReports(&pending));
+ completed.clear();
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->GetCompletedReports(&completed));
+
+ ASSERT_EQ(1u, pending.size());
+ ASSERT_EQ(4u, completed.size());
+
+ EXPECT_EQ(report_0_uuid, pending[0].uuid);
+
+ for (const auto& report : completed) {
+ if (report.uuid == report_3_uuid) {
+ EXPECT_FALSE(report.uploaded);
+ EXPECT_EQ(0, report.upload_attempts);
+ EXPECT_EQ(0, report.last_upload_attempt_time);
+ } else {
+ EXPECT_TRUE(report.uploaded);
+ EXPECT_GT(report.upload_attempts, 0);
+ EXPECT_GT(report.last_upload_attempt_time, 0);
+ }
+ EXPECT_FALSE(report.file_path.empty());
+ }
+}
+
+TEST_F(CrashReportDatabaseTest, DuelingUploads) {
+ CrashReportDatabase::Report report;
+ CreateCrashReport(&report);
+
+ const CrashReportDatabase::Report* upload_report;
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->GetReportForUploading(report.uuid, &upload_report));
+
+ const CrashReportDatabase::Report* upload_report_2 = nullptr;
+ EXPECT_EQ(CrashReportDatabase::kBusyError,
+ db()->GetReportForUploading(report.uuid, &upload_report_2));
+ EXPECT_FALSE(upload_report_2);
+
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->RecordUploadAttempt(upload_report, true, std::string()));
+}
+
+TEST_F(CrashReportDatabaseTest, MoveDatabase) {
+ CrashReportDatabase::NewReport* new_report;
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->PrepareNewCrashReport(&new_report));
+ EXPECT_TRUE(FileExistsAtPath(new_report->path)) << new_report->path.value();
+ UUID uuid;
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->FinishedWritingCrashReport(new_report, &uuid));
+
+ RelocateDatabase();
+
+ CrashReportDatabase::Report report;
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->LookUpCrashReport(uuid, &report));
+ ExpectPreparedCrashReport(report);
+ EXPECT_TRUE(FileExistsAtPath(report.file_path)) << report.file_path.value();
+}
+
+TEST_F(CrashReportDatabaseTest, ReportRemoved) {
+ CrashReportDatabase::NewReport* new_report;
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->PrepareNewCrashReport(&new_report));
+ EXPECT_TRUE(FileExistsAtPath(new_report->path)) << new_report->path.value();
+ UUID uuid;
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->FinishedWritingCrashReport(new_report, &uuid));
+
+ CrashReportDatabase::Report report;
+ EXPECT_EQ(CrashReportDatabase::kNoError,
+ db()->LookUpCrashReport(uuid, &report));
+
+#if defined(OS_WIN)
+ EXPECT_EQ(0, _wunlink(report.file_path.value().c_str()));
+#else
+ EXPECT_EQ(0, unlink(report.file_path.value().c_str()))
+ << ErrnoMessage("unlink");
+#endif
+
+ EXPECT_EQ(CrashReportDatabase::kReportNotFound,
+ db()->LookUpCrashReport(uuid, &report));
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/client/crash_report_database_win.cc b/chromium/third_party/crashpad/crashpad/client/crash_report_database_win.cc
new file mode 100644
index 00000000000..aea06421545
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/crash_report_database_win.cc
@@ -0,0 +1,762 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "client/crash_report_database.h"
+
+#include <string.h>
+#include <time.h>
+#include <windows.h>
+
+#include "base/logging.h"
+#include "base/numerics/safe_math.h"
+#include "base/strings/string16.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "client/settings.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+
+namespace {
+
+const wchar_t kReportsDirectory[] = L"reports";
+const wchar_t kMetadataFileName[] = L"metadata";
+
+const wchar_t kSettings[] = L"settings.dat";
+
+const wchar_t kCrashReportFileExtension[] = L"dmp";
+
+const uint32_t kMetadataFileHeaderMagic = 'CPAD';
+const uint32_t kMetadataFileVersion = 1;
+
+using OperationStatus = CrashReportDatabase::OperationStatus;
+
+// Helpers ---------------------------------------------------------------------
+
+// Adds a string to the string table and returns the byte index where it was
+// added.
+uint32_t AddStringToTable(std::string* string_table, const std::string& str) {
+ uint32_t offset = base::checked_cast<uint32_t>(string_table->size());
+ *string_table += str;
+ *string_table += '\0';
+ return offset;
+}
+
+// Converts |str| to UTF8, adds the result to the string table and returns the
+// byte index where it was added.
+uint32_t AddStringToTable(std::string* string_table,
+ const base::string16& str) {
+ return AddStringToTable(string_table, base::UTF16ToUTF8(str));
+}
+
+// Reads from the current file position to EOF and returns as a string of bytes.
+std::string ReadRestOfFileAsString(FileHandle file) {
+ FileOffset read_from = LoggingSeekFile(file, 0, SEEK_CUR);
+ FileOffset end = LoggingSeekFile(file, 0, SEEK_END);
+ FileOffset original = LoggingSeekFile(file, read_from, SEEK_SET);
+ if (read_from == -1 || end == -1 || original == -1 || read_from == end)
+ return std::string();
+ DCHECK_EQ(read_from, original);
+ DCHECK_GT(end, read_from);
+ size_t data_length = static_cast<size_t>(end - read_from);
+ std::string buffer(data_length, '\0');
+ return LoggingReadFile(file, &buffer[0], data_length) ? buffer
+ : std::string();
+}
+
+// Helper structures, and conversions ------------------------------------------
+
+// The format of the on disk metadata file is a MetadataFileHeader, followed by
+// a number of fixed size records of MetadataFileReportRecord, followed by a
+// string table in UTF8 format, where each string is \0 terminated.
+struct MetadataFileHeader {
+ uint32_t magic;
+ uint32_t version;
+ uint32_t num_records;
+ uint32_t padding;
+};
+
+struct ReportDisk;
+
+enum class ReportState {
+ //! \brief Created and filled out by caller, owned by database.
+ kPending,
+ //! \brief In the process of uploading, owned by caller.
+ kUploading,
+ //! \brief Upload completed or skipped, owned by database.
+ kCompleted,
+};
+
+struct MetadataFileReportRecord {
+ // Note that this default constructor does no initialization. It is used only
+ // to create an array of records that are immediately initialized by reading
+ // from disk in Metadata::Read().
+ MetadataFileReportRecord() {}
+
+ // Constructs from a ReportDisk, adding to |string_table| and storing indices
+ // as strings into that table.
+ MetadataFileReportRecord(const ReportDisk& report, std::string* string_table);
+
+ UUID uuid; // UUID is a 16 byte, standard layout structure.
+ uint32_t file_path_index; // Index into string table. File name is relative
+ // to the reports directory when on disk.
+ uint32_t id_index; // Index into string table.
+ int64_t creation_time; // Holds a time_t.
+ int64_t last_upload_attempt_time; // Holds a time_t.
+ int32_t upload_attempts;
+ int32_t state; // A ReportState.
+ uint8_t uploaded; // Boolean, 0 or 1.
+ uint8_t padding[7];
+};
+
+//! \brief A private extension of the Report class that includes additional data
+//! that's stored on disk in the metadata file.
+struct ReportDisk : public CrashReportDatabase::Report {
+ ReportDisk(const MetadataFileReportRecord& record,
+ const base::FilePath& report_dir,
+ const std::string& string_table);
+
+ ReportDisk(const UUID& uuid,
+ const base::FilePath& path,
+ time_t creation_tim,
+ ReportState state);
+
+ //! \brief The current state of the report.
+ ReportState state;
+};
+
+MetadataFileReportRecord::MetadataFileReportRecord(const ReportDisk& report,
+ std::string* string_table)
+ : uuid(report.uuid),
+ file_path_index(
+ AddStringToTable(string_table, report.file_path.BaseName().value())),
+ id_index(AddStringToTable(string_table, report.id)),
+ creation_time(report.creation_time),
+ last_upload_attempt_time(report.last_upload_attempt_time),
+ upload_attempts(report.upload_attempts),
+ state(static_cast<uint32_t>(report.state)),
+ uploaded(report.uploaded) {
+ memset(&padding, 0, sizeof(padding));
+}
+
+ReportDisk::ReportDisk(const MetadataFileReportRecord& record,
+ const base::FilePath& report_dir,
+ const std::string& string_table)
+ : Report() {
+ uuid = record.uuid;
+ file_path = report_dir.Append(
+ base::UTF8ToUTF16(&string_table[record.file_path_index]));
+ id = &string_table[record.id_index];
+ creation_time = record.creation_time;
+ uploaded = record.uploaded;
+ last_upload_attempt_time = record.last_upload_attempt_time;
+ upload_attempts = record.upload_attempts;
+ state = static_cast<ReportState>(record.state);
+}
+
+ReportDisk::ReportDisk(const UUID& uuid,
+ const base::FilePath& path,
+ time_t creation_time,
+ ReportState state)
+ : Report() {
+ this->uuid = uuid;
+ this->file_path = path;
+ this->creation_time = creation_time;
+ this->state = state;
+}
+
+// Metadata --------------------------------------------------------------------
+
+//! \brief Manages the metadata for the set of reports, handling serialization
+//! to disk, and queries.
+class Metadata {
+ public:
+ //! \brief Writes any changes if necessary, unlocks and closes the file
+ //! handle.
+ ~Metadata();
+
+ static scoped_ptr<Metadata> Create(const base::FilePath& metadata_file,
+ const base::FilePath& report_dir);
+
+ //! \brief Adds a new report to the set.
+ //!
+ //! \param[in] new_report_disk The record to add. The #state field must be set
+ //! to kPending.
+ void AddNewRecord(const ReportDisk& new_report_disk);
+
+ //! \brief Finds all reports in a given state. The \a reports vector is only
+ //! valid when CrashReportDatabase::kNoError is returned.
+ //!
+ //! \param[in] desired_state The state to match.
+ //! \param[out] reports Matching reports, must be empty on entry.
+ OperationStatus FindReports(
+ ReportState desired_state,
+ std::vector<CrashReportDatabase::Report>* reports) const;
+
+ //! \brief Finds the report matching the given UUID.
+ //!
+ //! The returned report is only valid if CrashReportDatabase::kNoError is
+ //! returned.
+ //!
+ //! \param[in] uuid The report identifier.
+ //! \param[out] report_disk The found report, valid only if
+ //! CrashReportDatabase::kNoError is returned. Ownership is not
+ //! transferred to the caller, and the report may not be modified.
+ OperationStatus FindSingleReport(const UUID& uuid,
+ const ReportDisk** report_disk) const;
+
+ //! \brief Finds a single report matching the given UUID and in the desired
+ //! state, and returns a mutable ReportDisk* if found.
+ //!
+ //! This marks the metadata as dirty, and on destruction, changes will be
+ //! written to disk via Write().
+ //!
+ //! \return #kNoError on success. #kReportNotFound if there was no report with
+ //! the specified UUID. #kBusyError if the report was not in the specified
+ //! state.
+ OperationStatus FindSingleReportAndMarkDirty(const UUID& uuid,
+ ReportState desired_state,
+ ReportDisk** report_disk);
+
+ private:
+ Metadata(FileHandle handle, const base::FilePath& report_dir);
+
+ bool Rewind();
+
+ void Read();
+ void Write();
+
+ //! \brief Confirms that the corresponding report actually exists on disk
+ //! (that is, the dump file has not been removed), and that the report is
+ //! in the given state.
+ static OperationStatus VerifyReport(const ReportDisk& report_disk,
+ ReportState desired_state);
+ //! \brief Confirms that the corresponding report actually exists on disk
+ //! (that is, the dump file has not been removed).
+ static OperationStatus VerifyReportAnyState(const ReportDisk& report_disk);
+
+ ScopedFileHandle handle_;
+ const base::FilePath report_dir_;
+ bool dirty_; //! \brief `true` when a Write() is required on destruction.
+ std::vector<ReportDisk> reports_;
+
+ DISALLOW_COPY_AND_ASSIGN(Metadata);
+};
+
+Metadata::~Metadata() {
+ if (dirty_)
+ Write();
+ // Not actually async, UnlockFileEx requires the Offset fields.
+ OVERLAPPED overlapped = {0};
+ if (!UnlockFileEx(handle_.get(), 0, MAXDWORD, MAXDWORD, &overlapped))
+ PLOG(ERROR) << "UnlockFileEx";
+}
+
+// static
+scoped_ptr<Metadata> Metadata::Create(const base::FilePath& metadata_file,
+ const base::FilePath& report_dir) {
+ // It is important that dwShareMode be non-zero so that concurrent access to
+ // this file results in a successful open. This allows us to get to LockFileEx
+ // which then blocks to guard access.
+ FileHandle handle = CreateFile(metadata_file.value().c_str(),
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ nullptr);
+ if (handle == kInvalidFileHandle)
+ return scoped_ptr<Metadata>();
+ // Not actually async, LockFileEx requires the Offset fields.
+ OVERLAPPED overlapped = {0};
+ if (!LockFileEx(handle,
+ LOCKFILE_EXCLUSIVE_LOCK,
+ 0,
+ MAXDWORD,
+ MAXDWORD,
+ &overlapped)) {
+ PLOG(ERROR) << "LockFileEx";
+ return scoped_ptr<Metadata>();
+ }
+
+ scoped_ptr<Metadata> metadata(new Metadata(handle, report_dir));
+ // If Read() fails, for whatever reason (corruption, etc.) metadata will not
+ // have been modified and will be in a clean empty state. We continue on and
+ // return an empty database to hopefully recover. This means that existing
+ // crash reports have been orphaned.
+ metadata->Read();
+ return metadata;
+}
+
+void Metadata::AddNewRecord(const ReportDisk& new_report_disk) {
+ DCHECK(new_report_disk.state == ReportState::kPending);
+ reports_.push_back(new_report_disk);
+ dirty_ = true;
+}
+
+OperationStatus Metadata::FindReports(
+ ReportState desired_state,
+ std::vector<CrashReportDatabase::Report>* reports) const {
+ DCHECK(reports->empty());
+ for (const auto& report : reports_) {
+ if (report.state == desired_state &&
+ VerifyReport(report, desired_state) == CrashReportDatabase::kNoError) {
+ reports->push_back(report);
+ }
+ }
+ return CrashReportDatabase::kNoError;
+}
+
+OperationStatus Metadata::FindSingleReport(
+ const UUID& uuid,
+ const ReportDisk** out_report) const {
+ auto report_iter = std::find_if(
+ reports_.begin(), reports_.end(), [uuid](const ReportDisk& report) {
+ return report.uuid == uuid;
+ });
+ if (report_iter == reports_.end())
+ return CrashReportDatabase::kReportNotFound;
+ OperationStatus os = VerifyReportAnyState(*report_iter);
+ if (os == CrashReportDatabase::kNoError)
+ *out_report = &*report_iter;
+ return os;
+}
+
+OperationStatus Metadata::FindSingleReportAndMarkDirty(
+ const UUID& uuid,
+ ReportState desired_state,
+ ReportDisk** report_disk) {
+ auto report_iter = std::find_if(
+ reports_.begin(), reports_.end(), [uuid](const ReportDisk& report) {
+ return report.uuid == uuid;
+ });
+ if (report_iter == reports_.end())
+ return CrashReportDatabase::kReportNotFound;
+ OperationStatus os = VerifyReport(*report_iter, desired_state);
+ if (os == CrashReportDatabase::kNoError) {
+ dirty_ = true;
+ *report_disk = &*report_iter;
+ }
+ return os;
+}
+
+Metadata::Metadata(FileHandle handle, const base::FilePath& report_dir)
+ : handle_(handle), report_dir_(report_dir), dirty_(false), reports_() {
+}
+
+bool Metadata::Rewind() {
+ FileOffset result = LoggingSeekFile(handle_.get(), 0, SEEK_SET);
+ DCHECK_EQ(result, 0);
+ return result == 0;
+}
+
+void Metadata::Read() {
+ FileOffset length = LoggingSeekFile(handle_.get(), 0, SEEK_END);
+ if (length <= 0) // Failed, or empty: Abort.
+ return;
+ if (!Rewind()) {
+ LOG(ERROR) << "failed to rewind to read";
+ return;
+ }
+
+ MetadataFileHeader header;
+ if (!LoggingReadFile(handle_.get(), &header, sizeof(header))) {
+ LOG(ERROR) << "failed to read header";
+ return;
+ }
+ if (header.magic != kMetadataFileHeaderMagic ||
+ header.version != kMetadataFileVersion) {
+ LOG(ERROR) << "unexpected header";
+ return;
+ }
+
+ base::CheckedNumeric<uint32_t> records_size =
+ base::CheckedNumeric<uint32_t>(header.num_records) *
+ static_cast<uint32_t>(sizeof(MetadataFileReportRecord));
+ if (!records_size.IsValid()) {
+ LOG(ERROR) << "record size out of range";
+ return;
+ }
+
+ std::vector<MetadataFileReportRecord> records(header.num_records);
+ if (!LoggingReadFile(handle_.get(), &records[0], records_size.ValueOrDie())) {
+ LOG(ERROR) << "failed to read records";
+ return;
+ }
+
+ std::string string_table = ReadRestOfFileAsString(handle_.get());
+ if (string_table.empty() || string_table.back() != '\0') {
+ LOG(ERROR) << "bad string table";
+ return;
+ }
+
+ std::vector<ReportDisk> reports;
+ for (const auto& record : records) {
+ if (record.file_path_index >= string_table.size() ||
+ record.id_index >= string_table.size()) {
+ LOG(ERROR) << "invalid string table index";
+ return;
+ }
+ reports.push_back(ReportDisk(record, report_dir_, string_table));
+ }
+ reports_.swap(reports);
+}
+
+void Metadata::Write() {
+ if (!Rewind()) {
+ LOG(ERROR) << "failed to rewind to write";
+ return;
+ }
+
+ // Truncate to ensure that a partial write doesn't cause a mix of old and new
+ // data causing an incorrect interpretation on read.
+ if (!SetEndOfFile(handle_.get())) {
+ PLOG(ERROR) << "failed to truncate";
+ return;
+ }
+
+ size_t num_records = reports_.size();
+
+ // Fill and write out the header.
+ MetadataFileHeader header = {0};
+ header.magic = kMetadataFileHeaderMagic;
+ header.version = kMetadataFileVersion;
+ header.num_records = base::checked_cast<uint32_t>(num_records);
+ if (!LoggingWriteFile(handle_.get(), &header, sizeof(header))) {
+ LOG(ERROR) << "failed to write header";
+ return;
+ }
+
+ // Build the records and string table we're going to write.
+ std::string string_table;
+ std::vector<MetadataFileReportRecord> records;
+ records.reserve(num_records);
+ for (const auto& report : reports_) {
+ const base::FilePath& path = report.file_path;
+ if (path.DirName() != report_dir_) {
+ LOG(ERROR) << path.value().c_str() << " expected to start with "
+ << base::UTF16ToUTF8(report_dir_.value());
+ return;
+ }
+ records.push_back(MetadataFileReportRecord(report, &string_table));
+ }
+
+ if (!LoggingWriteFile(handle_.get(),
+ &records[0],
+ records.size() * sizeof(MetadataFileReportRecord))) {
+ LOG(ERROR) << "failed to write records";
+ return;
+ }
+ if (!LoggingWriteFile(
+ handle_.get(), string_table.c_str(), string_table.size())) {
+ LOG(ERROR) << "failed to write string table";
+ return;
+ }
+}
+
+// static
+OperationStatus Metadata::VerifyReportAnyState(const ReportDisk& report_disk) {
+ DWORD fileattr = GetFileAttributes(report_disk.file_path.value().c_str());
+ if (fileattr == INVALID_FILE_ATTRIBUTES)
+ return CrashReportDatabase::kReportNotFound;
+ return (fileattr & FILE_ATTRIBUTE_DIRECTORY)
+ ? CrashReportDatabase::kFileSystemError
+ : CrashReportDatabase::kNoError;
+}
+
+// static
+OperationStatus Metadata::VerifyReport(const ReportDisk& report_disk,
+ ReportState desired_state) {
+ return (report_disk.state == desired_state)
+ ? VerifyReportAnyState(report_disk)
+ : CrashReportDatabase::kBusyError;
+}
+
+//! \brief Ensures that the node at path is a directory, and creates it if it
+//! does not exist.
+//!
+//! \return If the path points to a file, rather than a directory, or the
+//! directory could not be created, returns `false`. Otherwise, returns
+//! `true`, indicating that path already was or now is a directory.
+bool CreateDirectoryIfNecessary(const base::FilePath& path) {
+ if (CreateDirectory(path.value().c_str(), nullptr))
+ return true;
+ if (GetLastError() != ERROR_ALREADY_EXISTS) {
+ PLOG(ERROR) << "CreateDirectory";
+ return false;
+ }
+ DWORD fileattr = GetFileAttributes(path.value().c_str());
+ if (fileattr == INVALID_FILE_ATTRIBUTES) {
+ PLOG(ERROR) << "GetFileAttributes";
+ return false;
+ }
+ if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0)
+ return true;
+ LOG(ERROR) << "not a directory";
+ return false;
+}
+
+// CrashReportDatabaseWin ------------------------------------------------------
+
+class CrashReportDatabaseWin : public CrashReportDatabase {
+ public:
+ explicit CrashReportDatabaseWin(const base::FilePath& path);
+ ~CrashReportDatabaseWin() override;
+
+ bool Initialize();
+
+ // CrashReportDatabase:
+ Settings* GetSettings() override;
+ OperationStatus PrepareNewCrashReport(NewReport** report) override;
+ OperationStatus FinishedWritingCrashReport(NewReport* report,
+ UUID* uuid) override;
+ OperationStatus ErrorWritingCrashReport(NewReport* report) override;
+ OperationStatus LookUpCrashReport(const UUID& uuid, Report* report) override;
+ OperationStatus GetPendingReports(std::vector<Report>* reports) override;
+ OperationStatus GetCompletedReports(std::vector<Report>* reports) override;
+ OperationStatus GetReportForUploading(const UUID& uuid,
+ const Report** report) override;
+ OperationStatus RecordUploadAttempt(const Report* report,
+ bool successful,
+ const std::string& id) override;
+ OperationStatus SkipReportUpload(const UUID& uuid) override;
+
+ private:
+ scoped_ptr<Metadata> AcquireMetadata();
+
+ base::FilePath base_dir_;
+ Settings settings_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrashReportDatabaseWin);
+};
+
+CrashReportDatabaseWin::CrashReportDatabaseWin(const base::FilePath& path)
+ : CrashReportDatabase(),
+ base_dir_(path),
+ settings_(base_dir_.Append(kSettings)),
+ initialized_() {
+}
+
+CrashReportDatabaseWin::~CrashReportDatabaseWin() {
+}
+
+bool CrashReportDatabaseWin::Initialize() {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ // Ensure the database and report subdirectories exist.
+ if (!CreateDirectoryIfNecessary(base_dir_) ||
+ !CreateDirectoryIfNecessary(base_dir_.Append(kReportsDirectory)))
+ return false;
+
+ // TODO(scottmg): When are completed reports pruned from disk? Delete here or
+ // maybe on AcquireMetadata().
+
+ if (!settings_.Initialize())
+ return false;
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+Settings* CrashReportDatabaseWin::GetSettings() {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return &settings_;
+}
+
+OperationStatus CrashReportDatabaseWin::PrepareNewCrashReport(
+ NewReport** report) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ scoped_ptr<NewReport> new_report(new NewReport());
+ if (!new_report->uuid.InitializeWithNew())
+ return kFileSystemError;
+ new_report->path = base_dir_.Append(kReportsDirectory)
+ .Append(new_report->uuid.ToString16() + L"." +
+ kCrashReportFileExtension);
+ new_report->handle = LoggingOpenFileForWrite(new_report->path,
+ FileWriteMode::kCreateOrFail,
+ FilePermissions::kOwnerOnly);
+ if (new_report->handle == INVALID_HANDLE_VALUE)
+ return kFileSystemError;
+
+ *report = new_report.release();
+ return kNoError;
+}
+
+OperationStatus CrashReportDatabaseWin::FinishedWritingCrashReport(
+ NewReport* report,
+ UUID* uuid) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ // Take ownership of the report.
+ scoped_ptr<NewReport> scoped_report(report);
+ // Take ownership of the file handle.
+ ScopedFileHandle handle(report->handle);
+
+ scoped_ptr<Metadata> metadata(AcquireMetadata());
+ if (!metadata)
+ return kDatabaseError;
+ metadata->AddNewRecord(ReportDisk(scoped_report->uuid,
+ scoped_report->path,
+ time(nullptr),
+ ReportState::kPending));
+ *uuid = scoped_report->uuid;
+ return kNoError;
+}
+
+OperationStatus CrashReportDatabaseWin::ErrorWritingCrashReport(
+ NewReport* report) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ // Take ownership of the report.
+ scoped_ptr<NewReport> scoped_report(report);
+
+ // Close the outstanding handle.
+ LoggingCloseFile(report->handle);
+
+ // We failed to write, so remove the dump file. There's no entry in the
+ // metadata table yet.
+ if (!DeleteFile(scoped_report->path.value().c_str())) {
+ PLOG(ERROR) << "DeleteFile "
+ << base::UTF16ToUTF8(scoped_report->path.value());
+ return CrashReportDatabase::kFileSystemError;
+ }
+
+ return kNoError;
+}
+
+OperationStatus CrashReportDatabaseWin::LookUpCrashReport(const UUID& uuid,
+ Report* report) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ scoped_ptr<Metadata> metadata(AcquireMetadata());
+ if (!metadata)
+ return kDatabaseError;
+ // Find and return a copy of the matching report.
+ const ReportDisk* report_disk;
+ OperationStatus os = metadata->FindSingleReport(uuid, &report_disk);
+ if (os == kNoError)
+ *report = *report_disk;
+ return os;
+}
+
+OperationStatus CrashReportDatabaseWin::GetPendingReports(
+ std::vector<Report>* reports) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ scoped_ptr<Metadata> metadata(AcquireMetadata());
+ return metadata ? metadata->FindReports(ReportState::kPending, reports)
+ : kDatabaseError;
+}
+
+OperationStatus CrashReportDatabaseWin::GetCompletedReports(
+ std::vector<Report>* reports) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ scoped_ptr<Metadata> metadata(AcquireMetadata());
+ return metadata ? metadata->FindReports(ReportState::kCompleted, reports)
+ : kDatabaseError;
+}
+
+OperationStatus CrashReportDatabaseWin::GetReportForUploading(
+ const UUID& uuid,
+ const Report** report) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ scoped_ptr<Metadata> metadata(AcquireMetadata());
+ if (!metadata)
+ return kDatabaseError;
+ // TODO(scottmg): After returning this report to the client, there is no way
+ // to reap this report if the uploader fails to call RecordUploadAttempt() or
+ // SkipReportUpload() (if it crashed or was otherwise buggy). To resolve this,
+ // one possibility would be to change the interface to be FileHandle based, so
+ // that instead of giving the file_path back to the client and changing state
+ // to kUploading, we return an exclusive access handle, and use that as the
+ // signal that the upload is pending, rather than an update to state in the
+ // metadata. Alternatively, there could be a "garbage collection" at startup
+ // where any reports that are orphaned in the kUploading state are either
+ // reset to kPending to retry, or discarded.
+ ReportDisk* report_disk;
+ OperationStatus os = metadata->FindSingleReportAndMarkDirty(
+ uuid, ReportState::kPending, &report_disk);
+ if (os == CrashReportDatabase::kNoError) {
+ report_disk->state = ReportState::kUploading;
+ // Create a copy for passing back to client. This will be freed in
+ // RecordUploadAttempt.
+ *report = new Report(*report_disk);
+ }
+ return os;
+}
+
+OperationStatus CrashReportDatabaseWin::RecordUploadAttempt(
+ const Report* report,
+ bool successful,
+ const std::string& id) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ // Take ownership, allocated in GetReportForUploading.
+ scoped_ptr<const Report> upload_report(report);
+ scoped_ptr<Metadata> metadata(AcquireMetadata());
+ if (!metadata)
+ return kDatabaseError;
+ ReportDisk* report_disk;
+ OperationStatus os = metadata->FindSingleReportAndMarkDirty(
+ report->uuid, ReportState::kUploading, &report_disk);
+ if (os == CrashReportDatabaseWin::kNoError) {
+ report_disk->uploaded = successful;
+ report_disk->id = id;
+ report_disk->last_upload_attempt_time = time(nullptr);
+ report_disk->upload_attempts++;
+ report_disk->state =
+ successful ? ReportState::kCompleted : ReportState::kPending;
+ }
+
+ // Call Settings::SetLastUploadAttemptTime().
+ // https://code.google.com/p/crashpad/issues/detail?id=13.
+
+ return os;
+}
+
+OperationStatus CrashReportDatabaseWin::SkipReportUpload(const UUID& uuid) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ scoped_ptr<Metadata> metadata(AcquireMetadata());
+ if (!metadata)
+ return kDatabaseError;
+ ReportDisk* report_disk;
+ OperationStatus os = metadata->FindSingleReportAndMarkDirty(
+ uuid, ReportState::kPending, &report_disk);
+ if (os == CrashReportDatabase::kNoError)
+ report_disk->state = ReportState::kCompleted;
+ return os;
+}
+
+scoped_ptr<Metadata> CrashReportDatabaseWin::AcquireMetadata() {
+ base::FilePath metadata_file = base_dir_.Append(kMetadataFileName);
+ return Metadata::Create(metadata_file, base_dir_.Append(kReportsDirectory));
+}
+
+} // namespace
+
+// static
+scoped_ptr<CrashReportDatabase> CrashReportDatabase::Initialize(
+ const base::FilePath& path) {
+ scoped_ptr<CrashReportDatabaseWin> database_win(
+ new CrashReportDatabaseWin(path));
+ return database_win->Initialize() ? database_win.Pass()
+ : scoped_ptr<CrashReportDatabaseWin>();
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/client/crashpad_client.h b/chromium/third_party/crashpad/crashpad/client/crashpad_client.h
new file mode 100644
index 00000000000..27d6111b21c
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/crashpad_client.h
@@ -0,0 +1,126 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_CLIENT_CRASHPAD_CLIENT_H_
+#define CRASHPAD_CLIENT_CRASHPAD_CLIENT_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "build/build_config.h"
+
+#if defined(OS_MACOSX)
+#include "base/mac/scoped_mach_port.h"
+#endif
+
+namespace crashpad {
+
+//! \brief The primary interface for an application to have Crashpad monitor
+//! it for crashes.
+class CrashpadClient {
+ public:
+ CrashpadClient();
+ ~CrashpadClient();
+
+ //! \brief Starts a Crashpad handler process, performing any necessary
+ //! handshake to configure it.
+ //!
+ //! This method does not actually direct any crashes to the Crashpad handler,
+ //! because there may be alternative ways to use an existing Crashpad handler
+ //! without having to start one. To begin directing crashes to the handler,
+ //! started by this method, call UseHandler() after this method returns
+ //! successfully.
+ //!
+ //! On Mac OS X, this method starts a Crashpad handler and obtains a Mach
+ //! send right corresponding to a receive right held by the handler process.
+ //! The handler process runs an exception server on this port.
+ //!
+ //! On Windows, SetHandler() is normally used instead since the handler is
+ //! started by other means.
+ //!
+ //! \param[in] handler The path to a Crashpad handler executable.
+ //! \param[in] database The path to a Crashpad database. The handler will be
+ //! started with this path as its `--database` argument.
+ //! \param[in] url The URL of an upload server. The handler will be started
+ //! with this URL as its `--url` argument.
+ //! \param[in] annotations Process annotations to set in each crash report.
+ //! The handler will be started with an `--annotation` argument for each
+ //! element in this map.
+ //! \param[in] arguments Additional arguments to pass to the Crashpad handler.
+ //! Arguments passed in other parameters and arguments required to perform
+ //! the handshake are the responsibility of this method, and must not be
+ //! specified in this parameter.
+ //!
+ //! \return `true` on success, `false` on failure with a message logged.
+ bool StartHandler(const base::FilePath& handler,
+ const base::FilePath& database,
+ const std::string& url,
+ const std::map<std::string, std::string>& annotations,
+ const std::vector<std::string>& arguments);
+
+#if defined(OS_WIN) || DOXYGEN
+ //! \brief Sets the IPC port of a presumably-running Crashpad handler process
+ //! which was started with StartHandler() or by other compatible means
+ //! and does an IPC message exchange to register this process with the
+ //! handler. However, just like StartHandler(), crashes are not serviced
+ //! until UseHandler() is called.
+ //!
+ //! The IPC port name (somehow) encodes enough information so that
+ //! registration is done with a crash handler using the appropriate database
+ //! and upload server.
+ //!
+ //! \param[in] ipc_port The full name of the crash handler IPC port.
+ //!
+ //! \return `true` on success and `false` on failure.
+ bool SetHandler(const std::string& ipc_port);
+#endif
+
+ //! \brief Configures the process to direct its crashes to a Crashpad handler.
+ //!
+ //! The Crashpad handler must previously have been started by StartHandler()
+ //! or configured by SetHandler().
+ //!
+ //! On Mac OS X, this method sets the task’s exception port for `EXC_CRASH`,
+ //! `EXC_RESOURCE`, and `EXC_GUARD` exceptions to the Mach send right obtained
+ //! by StartHandler(). The handler will be installed with behavior
+ //! `EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES` and thread state flavor
+ //! `MACHINE_THREAD_STATE`. Exception ports are inherited, so a Crashpad
+ //! handler chosen by UseHandler() will remain the handler for any child
+ //! processes created after UseHandler() is called. Child processes do not
+ //! need to call StartHandler() or UseHandler() or be aware of Crashpad in any
+ //! way. The Crashpad handler will receive crashes from child processes that
+ //! have inherited it as their exception handler even after the process that
+ //! called StartHandler() exits.
+ //!
+ //! On Windows, this method sets the unhandled exception handler to a local
+ //! function that when reached will "signal and wait" for the crash handler
+ //! process to create the dump.
+ //!
+ //! \return `true` on success, `false` on failure with a message logged.
+ bool UseHandler();
+
+ private:
+#if defined(OS_MACOSX)
+ base::mac::ScopedMachSendRight exception_port_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(CrashpadClient);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_CLIENT_CRASHPAD_CLIENT_H_
diff --git a/chromium/third_party/crashpad/crashpad/client/crashpad_client_mac.cc b/chromium/third_party/crashpad/crashpad/client/crashpad_client_mac.cc
new file mode 100644
index 00000000000..e97e58b15f3
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/crashpad_client_mac.cc
@@ -0,0 +1,227 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "client/crashpad_client.h"
+
+#include <mach/mach.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/stringprintf.h"
+#include "client/crashpad_client.h"
+#include "util/mach/child_port_handshake.h"
+#include "util/mach/exception_ports.h"
+#include "util/mach/mach_extensions.h"
+#include "util/posix/close_multiple.h"
+
+namespace {
+
+std::string FormatArgumentString(const std::string& name,
+ const std::string& value) {
+ return base::StringPrintf("--%s=%s", name.c_str(), value.c_str());
+}
+
+std::string FormatArgumentInt(const std::string& name, int value) {
+ return base::StringPrintf("--%s=%d", name.c_str(), value);
+}
+
+} // namespace
+
+namespace crashpad {
+
+CrashpadClient::CrashpadClient()
+ : exception_port_() {
+}
+
+CrashpadClient::~CrashpadClient() {
+}
+
+bool CrashpadClient::StartHandler(
+ const base::FilePath& handler,
+ const base::FilePath& database,
+ const std::string& url,
+ const std::map<std::string, std::string>& annotations,
+ const std::vector<std::string>& arguments) {
+ DCHECK_EQ(exception_port_, kMachPortNull);
+
+ // Set up the arguments for execve() first. These aren’t needed until execve()
+ // is called, but it’s dangerous to do this in a child process after fork().
+ ChildPortHandshake child_port_handshake;
+ int handshake_fd = child_port_handshake.ReadPipeFD();
+
+ // Use handler as argv[0], followed by arguments directed by this method’s
+ // parameters and a --handshake-fd argument. |arguments| are added first so
+ // that if it erroneously contains an argument such as --url, the actual |url|
+ // argument passed to this method will supersede it. In normal command-line
+ // processing, the last parameter wins in the case of a conflict.
+ std::vector<std::string> argv(1, handler.value());
+ argv.reserve(1 + arguments.size() + 2 + annotations.size() + 1);
+ for (const std::string& argument : arguments) {
+ argv.push_back(argument);
+ }
+ if (!database.value().empty()) {
+ argv.push_back(FormatArgumentString("database", database.value()));
+ }
+ if (!url.empty()) {
+ argv.push_back(FormatArgumentString("url", url));
+ }
+ for (const auto& kv : annotations) {
+ argv.push_back(
+ FormatArgumentString("annotation", kv.first + '=' + kv.second));
+ }
+ argv.push_back(FormatArgumentInt("handshake-fd", handshake_fd));
+
+ // argv_c contains const char* pointers and is terminated by nullptr. argv
+ // is required because the pointers in argv_c need to point somewhere, and
+ // they can’t point to temporaries such as those returned by
+ // FormatArgumentString().
+ std::vector<const char*> argv_c;
+ argv_c.reserve(argv.size() + 1);
+ for (const std::string& argument : argv) {
+ argv_c.push_back(argument.c_str());
+ }
+ argv_c.push_back(nullptr);
+
+ // Double-fork(). The three processes involved are parent, child, and
+ // grandchild. The grandchild will become the handler process. The child exits
+ // immediately after spawning the grandchild, so the grandchild becomes an
+ // orphan and its parent process ID becomes 1. This relieves the parent and
+ // child of the responsibility for reaping the grandchild with waitpid() or
+ // similar. The handler process is expected to outlive the parent process, so
+ // the parent shouldn’t be concerned with reaping it. This approach means that
+ // accidental early termination of the handler process will not result in a
+ // zombie process.
+ pid_t pid = fork();
+ if (pid < 0) {
+ PLOG(ERROR) << "fork";
+ return false;
+ }
+
+ if (pid == 0) {
+ // Child process.
+
+ // Call setsid(), creating a new process group and a new session, both led
+ // by this process. The new process group has no controlling terminal. This
+ // disconnects it from signals generated by the parent process’ terminal.
+ //
+ // setsid() is done in the child instead of the grandchild so that the
+ // grandchild will not be a session leader. If it were a session leader, an
+ // accidental open() of a terminal device without O_NOCTTY would make that
+ // terminal the controlling terminal.
+ //
+ // It’s not desirable for the handler to have a controlling terminal. The
+ // handler monitors clients on its own and manages its own lifetime, exiting
+ // when it loses all clients and when it deems it appropraite to do so. It
+ // may serve clients in different process groups or sessions than its
+ // original client, and receiving signals intended for its original client’s
+ // process group could be harmful in that case.
+ PCHECK(setsid() != -1) << "setsid";
+
+ pid = fork();
+ if (pid < 0) {
+ PLOG(FATAL) << "fork";
+ }
+
+ if (pid > 0) {
+ // Child process.
+
+ // _exit() instead of exit(), because fork() was called.
+ _exit(EXIT_SUCCESS);
+ }
+
+ // Grandchild process.
+
+ CloseMultipleNowOrOnExec(STDERR_FILENO + 1, handshake_fd);
+
+ // &argv_c[0] is a pointer to a pointer to const char data, but because of
+ // how C (not C++) works, execvp() wants a pointer to a const pointer to
+ // char data. It modifies neither the data nor the pointers, so the
+ // const_cast is safe.
+ execvp(handler.value().c_str(), const_cast<char* const*>(&argv_c[0]));
+ PLOG(FATAL) << "execvp " << handler.value();
+ }
+
+ // Parent process.
+
+ // waitpid() for the child, so that it does not become a zombie process. The
+ // child normally exits quickly.
+ int status;
+ pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0));
+ PCHECK(wait_pid != -1) << "waitpid";
+ DCHECK_EQ(wait_pid, pid);
+
+ if (WIFSIGNALED(status)) {
+ LOG(WARNING) << "intermediate process: signal " << WTERMSIG(status);
+ } else if (!WIFEXITED(status)) {
+ DLOG(WARNING) << "intermediate process: unknown termination " << status;
+ } else if (WEXITSTATUS(status) != EXIT_SUCCESS) {
+ LOG(WARNING) << "intermediate process: exit status " << WEXITSTATUS(status);
+ }
+
+ // Rendezvous with the handler running in the grandchild process.
+ exception_port_.reset(child_port_handshake.RunServer());
+
+ return exception_port_ ? true : false;
+}
+
+bool CrashpadClient::UseHandler() {
+ DCHECK_NE(exception_port_, kMachPortNull);
+
+ // Set the exception handler for EXC_CRASH, EXC_RESOURCE, and EXC_GUARD.
+ //
+ // EXC_CRASH is how most crashes are received. Most other exception types such
+ // as EXC_BAD_ACCESS are delivered to a host-level exception handler in the
+ // kernel where they are converted to POSIX signals. See 10.9.5
+ // xnu-2422.115.4/bsd/uxkern/ux_exception.c catch_mach_exception_raise(). If a
+ // core-generating signal (triggered through this hardware mechanism or a
+ // software mechanism such as abort() sending SIGABRT) is unhandled and the
+ // process exits, or if the process is killed with SIGKILL for code-signing
+ // reasons, an EXC_CRASH exception will be sent. See 10.9.5
+ // xnu-2422.115.4/bsd/kern/kern_exit.c proc_prepareexit().
+ //
+ // EXC_RESOURCE and EXC_GUARD do not become signals or EXC_CRASH exceptions.
+ // The host-level exception handler in the kernel does not receive these
+ // exception types, and even if it did, it would not map them to signals.
+ // Instead, the first Mach service loaded by the root (process ID 1) launchd
+ // with a boolean “ExceptionServer” property in its job dictionary (regardless
+ // of its value) or with any subdictionary property will become the host-level
+ // exception handler for EXC_CRASH, EXC_RESOURCE, and EXC_GUARD. See 10.9.5
+ // launchd-842.92.1/src/core.c job_setup_exception_port(). Normally, this job
+ // is com.apple.ReportCrash.Root, the systemwide Apple Crash Reporter. Since
+ // it is impossible to receive EXC_RESOURCE and EXC_GUARD exceptions through
+ // the EXC_CRASH mechanism, an exception handler must be registered for them
+ // by name if it is to receive these exception types. The default task-level
+ // handler for these exception types is set by launchd in a similar manner.
+ //
+ // EXC_MASK_RESOURCE and EXC_MASK_GUARD are not available on all systems, and
+ // the kernel will reject attempts to use them if it does not understand them,
+ // so AND them with ExcMaskAll(). EXC_MASK_CRASH is not present in
+ // ExcMaskAll() but is always supported. See the documentation for
+ // ExcMaskAll().
+ ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask, TASK_NULL);
+ if (!exception_ports.SetExceptionPort(
+ EXC_MASK_CRASH |
+ ((EXC_MASK_RESOURCE | EXC_MASK_GUARD) & ExcMaskAll()),
+ exception_port_,
+ EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES,
+ MACHINE_THREAD_STATE)) {
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/client/crashpad_client_win.cc b/chromium/third_party/crashpad/crashpad/client/crashpad_client_win.cc
new file mode 100644
index 00000000000..7eec54fb52a
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/crashpad_client_win.cc
@@ -0,0 +1,91 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "client/crashpad_client.h"
+
+#include <windows.h>
+
+#include "base/logging.h"
+
+namespace {
+// Time to wait for the handler to create a dump. This is tricky to figure out.
+const DWORD kMillisecondsUntilTerminate = 5000;
+
+// This is the exit code that the process will return to the system once the
+// crash has been handled by Crashpad. We don't want to clash with the
+// application-defined exit codes but we don't know them so we use one that is
+// unlikely to be used.
+const UINT kCrashExitCode = 0xffff7001;
+
+// These two handles to events are leaked.
+HANDLE g_signal_exception = nullptr;
+HANDLE g_wait_termination = nullptr;
+
+LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
+ // TODO (cpu): Here write |exception_pointers| to g_crashpad_info.
+ DWORD rv = SignalObjectAndWait(g_signal_exception,
+ g_wait_termination,
+ kMillisecondsUntilTerminate,
+ FALSE);
+ if (rv != WAIT_OBJECT_0) {
+ // Something went wrong. It is likely that a dump has not been created.
+ if (rv == WAIT_TIMEOUT) {
+ LOG(WARNING) << "SignalObjectAndWait timed out";
+ } else {
+ PLOG(WARNING) << "SignalObjectAndWait error";
+ }
+ }
+ // We don't want to generate more exceptions, so we take the fast route.
+ TerminateProcess(GetCurrentProcess(), kCrashExitCode);
+ return 0L;
+}
+
+} // namespace
+
+namespace crashpad {
+
+CrashpadClient::CrashpadClient() {
+}
+
+CrashpadClient::~CrashpadClient() {
+}
+
+bool CrashpadClient::StartHandler(
+ const base::FilePath& handler,
+ const base::FilePath& database,
+ const std::string& url,
+ const std::map<std::string, std::string>& annotations,
+ const std::vector<std::string>& arguments) {
+ // TODO(cpu): Provide a reference implementation.
+ return false;
+}
+
+bool SetHandler(const std::string& ipc_port) {
+ // TODO (cpu): Contact the handler and obtain g_signal_exception and
+ // g_wait_termination.
+ return false;
+}
+
+bool CrashpadClient::UseHandler() {
+ if (!g_signal_exception)
+ return false;
+ if (!g_wait_termination)
+ return false;
+ // In theory we could store the previous handler but it is not clear what
+ // use we have for it.
+ SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
+ return true;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/client/crashpad_info.cc b/chromium/third_party/crashpad/crashpad/client/crashpad_info.cc
new file mode 100644
index 00000000000..2fe3b1f7225
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/crashpad_info.cc
@@ -0,0 +1,96 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "client/crashpad_info.h"
+
+#include "build/build_config.h"
+#include "util/stdlib/cxx.h"
+
+#if defined(OS_MACOSX)
+#include <mach-o/loader.h>
+#endif
+
+#if CXX_LIBRARY_VERSION >= 2011
+#include <type_traits>
+#endif
+
+namespace {
+
+static const uint32_t kCrashpadInfoVersion = 1;
+
+} // namespace
+
+namespace crashpad {
+
+#if CXX_LIBRARY_VERSION >= 2011 || DOXYGEN
+// In C++11, check that CrashpadInfo has standard layout, which is what is
+// actually important.
+static_assert(std::is_standard_layout<CrashpadInfo>::value,
+ "CrashpadInfo must be standard layout");
+#else
+// In C++98 (ISO 14882), section 9.5.1 says that a union cannot have a member
+// with a non-trivial ctor, copy ctor, dtor, or assignment operator. Use this
+// property to ensure that CrashpadInfo remains POD. This doesn’t work for C++11
+// because the requirements for unions have been relaxed.
+union Compile_Assert {
+ CrashpadInfo Compile_Assert__CrashpadInfo_must_be_pod;
+};
+#endif
+
+// This structure needs to be stored somewhere that is easy to find without
+// external information.
+//
+// It isn’t placed in an unnamed namespace: hopefully, this will catch attempts
+// to place multiple copies of this structure into the same module. If that’s
+// attempted, and the name of the symbol is the same in each translation unit,
+// it will result in a linker error, which is better than having multiple
+// structures show up.
+//
+// This may result in a static module initializer in debug-mode builds, but
+// because it’s POD, no code should need to run to initialize this under
+// release-mode optimization.
+#if defined(OS_MACOSX)
+
+// Put the structure in __DATA,__crashpad_info where it can be easily found
+// without having to consult the symbol table. The “used” attribute prevents it
+// from being dead-stripped.
+__attribute__((section(SEG_DATA ",__crashpad_info"),
+ used,
+ visibility("hidden"))) CrashpadInfo g_crashpad_info;
+
+#elif defined(OS_WIN)
+
+// Put the struct in a section name CPADinfo where it can be found without the
+// symbol table.
+#pragma section("CPADinfo", read, write)
+__declspec(allocate("CPADinfo")) CrashpadInfo g_crashpad_info;
+
+#endif
+
+// static
+CrashpadInfo* CrashpadInfo::GetCrashpadInfo() {
+ return &g_crashpad_info;
+}
+
+CrashpadInfo::CrashpadInfo()
+ : signature_(kSignature),
+ size_(sizeof(*this)),
+ version_(kCrashpadInfoVersion),
+ crashpad_handler_behavior_(TriState::kUnset),
+ system_crash_reporter_forwarding_(TriState::kUnset),
+ padding_0_(0),
+ simple_annotations_(nullptr) {
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/client/crashpad_info.h b/chromium/third_party/crashpad/crashpad/client/crashpad_info.h
new file mode 100644
index 00000000000..5ce9fb8e8fd
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/crashpad_info.h
@@ -0,0 +1,128 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_CLIENT_CRASHPAD_INFO_H_
+#define CRASHPAD_CLIENT_CRASHPAD_INFO_H_
+
+#include "base/basictypes.h"
+
+#include <stdint.h>
+
+#include "client/simple_string_dictionary.h"
+#include "util/misc/tri_state.h"
+
+namespace crashpad {
+
+//! \brief A structure that can be used by a Crashpad-enabled program to
+//! provide information to the Crashpad crash handler.
+//!
+//! It is possible for one CrashpadInfo structure to appear in each loaded code
+//! module in a process, but from the perspective of the user of the client
+//! interface, there is only one global CrashpadInfo structure, located in the
+//! module that contains the client interface code.
+struct CrashpadInfo {
+ public:
+ //! \brief Returns the global CrashpadInfo structure.
+ static CrashpadInfo* GetCrashpadInfo();
+
+ CrashpadInfo();
+
+ //! \brief Sets the simple annotations dictionary.
+ //!
+ //! Simple annotations set on a CrashpadInfo structure are interpreted by
+ //! Crashpad as module-level annotations.
+ //!
+ //! Annotations may exist in \a simple_annotations at the time that this
+ //! method is called, or they may be added, removed, or modified in \a
+ //! simple_annotations after this method is called.
+ //!
+ //! \param[in] simple_annotations A dictionary that maps string keys to string
+ //! values. The CrashpadInfo object does not take ownership of the
+ //! SimpleStringDictionary object. It is the caller’s responsibility to
+ //! ensure that this pointer remains valid while it is in effect for a
+ //! CrashpadInfo object.
+ void set_simple_annotations(SimpleStringDictionary* simple_annotations) {
+ simple_annotations_ = simple_annotations;
+ }
+
+ //! \brief Enables or disables Crashpad handler processing.
+ //!
+ //! When handling an exception, the Crashpad handler will scan all modules in
+ //! a process. The first one that has a CrashpadInfo structure populated with
+ //! a value other than #kUnset for this field will dictate whether the handler
+ //! is functional or not. If all modules with a CrashpadInfo structure specify
+ //! #kUnset, the handler will be enabled. If disabled, the Crashpad handler
+ //! will still run and receive exceptions, but will not take any action on an
+ //! exception on its own behalf, except for the action necessary to determine
+ //! that it has been disabled.
+ //!
+ //! The Crashpad handler should not normally be disabled. More commonly, it
+ //! is appropraite to disable crash report upload by calling
+ //! Settings::SetUploadsEnabled().
+ void set_crashpad_handler_behavior(TriState crashpad_handler_behavior) {
+ crashpad_handler_behavior_ = crashpad_handler_behavior;
+ }
+
+ //! \brief Enables or disables Crashpad forwarding of exceptions to the
+ //! system’s crash reporter.
+ //!
+ //! When handling an exception, the Crashpad handler will scan all modules in
+ //! a process. The first one that has a CrashpadInfo structure populated with
+ //! a value other than #kUnset for this field will dictate whether the
+ //! exception is forwarded to the system’s crash reporter. If all modules with
+ //! a CrashpadInfo structure specify #kUnset, forwarding will be enabled.
+ //! Unless disabled, forwarding may still occur if the Crashpad handler is
+ //! disabled by SetCrashpadHandlerState(). Even when forwarding is enabled,
+ //! the Crashpad handler may choose not to forward all exceptions to the
+ //! system’s crash reporter in cases where it has reason to believe that the
+ //! system’s crash reporter would not normally have handled the exception in
+ //! Crashpad’s absence.
+ void set_system_crash_reporter_forwarding(
+ TriState system_crash_reporter_forwarding) {
+ system_crash_reporter_forwarding_ = system_crash_reporter_forwarding;
+ }
+
+ enum : uint32_t {
+ kSignature = 'CPad',
+ };
+
+ private:
+ // The compiler won’t necessarily see anyone using these fields, but it
+ // shouldn’t warn about that. These fields aren’t intended for use by the
+ // process they’re found in, they’re supposed to be read by the crash
+ // reporting process.
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-private-field"
+#endif
+
+ // Fields present in version 1:
+ uint32_t signature_; // kSignature
+ uint32_t size_; // The size of the entire CrashpadInfo structure.
+ uint32_t version_; // kVersion
+ TriState crashpad_handler_behavior_;
+ TriState system_crash_reporter_forwarding_;
+ uint16_t padding_0_;
+ SimpleStringDictionary* simple_annotations_; // weak
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(CrashpadInfo);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_CLIENT_CRASHPAD_INFO_H_
diff --git a/chromium/third_party/crashpad/crashpad/client/settings.cc b/chromium/third_party/crashpad/crashpad/client/settings.cc
new file mode 100644
index 00000000000..d1b35f1a7d6
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/settings.cc
@@ -0,0 +1,263 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "client/settings.h"
+
+#include <limits>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "util/numeric/in_range_cast.h"
+
+namespace crashpad {
+
+namespace internal {
+
+// static
+FileHandle ScopedLockedFileHandleTraits::InvalidValue() {
+ return kInvalidFileHandle;
+}
+
+// static
+void ScopedLockedFileHandleTraits::Free(FileHandle handle) {
+ if (handle != kInvalidFileHandle) {
+ LoggingUnlockFile(handle);
+ CheckedCloseFile(handle);
+ }
+}
+
+} // namespace internal
+
+struct ALIGNAS(4) Settings::Data {
+ static const uint32_t kSettingsMagic = 'CPds';
+ static const uint32_t kSettingsVersion = 1;
+
+ enum Options : uint32_t {
+ kUploadsEnabled = 1 << 0,
+ };
+
+ Data() : magic(kSettingsMagic),
+ version(kSettingsVersion),
+ options(0),
+ padding_0(0),
+ last_upload_attempt_time(0),
+ client_id() {}
+
+ uint32_t magic;
+ uint32_t version;
+ uint32_t options;
+ uint32_t padding_0;
+ uint64_t last_upload_attempt_time; // time_t
+ UUID client_id;
+};
+
+Settings::Settings(const base::FilePath& file_path)
+ : file_path_(file_path),
+ initialized_() {
+}
+
+Settings::~Settings() {
+}
+
+bool Settings::Initialize() {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ Data settings;
+ if (!OpenForWritingAndReadSettings(&settings).is_valid())
+ return false;
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+bool Settings::GetClientID(UUID* client_id) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ Data settings;
+ if (!OpenAndReadSettings(&settings))
+ return false;
+
+ *client_id = settings.client_id;
+ return true;
+}
+
+bool Settings::GetUploadsEnabled(bool* enabled) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ Data settings;
+ if (!OpenAndReadSettings(&settings))
+ return false;
+
+ *enabled = (settings.options & Data::Options::kUploadsEnabled) != 0;
+ return true;
+}
+
+bool Settings::SetUploadsEnabled(bool enabled) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ Data settings;
+ ScopedLockedFileHandle handle = OpenForWritingAndReadSettings(&settings);
+ if (!handle.is_valid())
+ return false;
+
+ if (enabled)
+ settings.options |= Data::Options::kUploadsEnabled;
+ else
+ settings.options &= ~Data::Options::kUploadsEnabled;
+
+ return WriteSettings(handle.get(), settings);
+}
+
+bool Settings::GetLastUploadAttemptTime(time_t* time) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ Data settings;
+ if (!OpenAndReadSettings(&settings))
+ return false;
+
+ *time = InRangeCast<time_t>(settings.last_upload_attempt_time,
+ std::numeric_limits<time_t>::max());
+ return true;
+}
+
+bool Settings::SetLastUploadAttemptTime(time_t time) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ Data settings;
+ ScopedLockedFileHandle handle = OpenForWritingAndReadSettings(&settings);
+ if (!handle.is_valid())
+ return false;
+
+ settings.last_upload_attempt_time = InRangeCast<uint64_t>(time, 0);
+
+ return WriteSettings(handle.get(), settings);
+}
+
+// static
+Settings::ScopedLockedFileHandle Settings::MakeScopedLockedFileHandle(
+ FileHandle file,
+ FileLocking locking) {
+ ScopedFileHandle scoped(file);
+ if (scoped.is_valid()) {
+ if (!LoggingLockFile(scoped.get(), locking))
+ scoped.reset();
+ }
+ return ScopedLockedFileHandle(scoped.release());
+}
+
+Settings::ScopedLockedFileHandle Settings::OpenForReading() {
+ return MakeScopedLockedFileHandle(LoggingOpenFileForRead(file_path()),
+ FileLocking::kShared);
+}
+
+Settings::ScopedLockedFileHandle Settings::OpenForReadingAndWriting() {
+ return MakeScopedLockedFileHandle(
+ LoggingOpenFileForReadAndWrite(file_path(),
+ FileWriteMode::kReuseOrCreate,
+ FilePermissions::kWorldReadable),
+ FileLocking::kExclusive);
+}
+
+bool Settings::OpenAndReadSettings(Data* out_data) {
+ ScopedLockedFileHandle handle = OpenForReading();
+ if (!handle.is_valid())
+ return false;
+
+ if (ReadSettings(handle.get(), out_data))
+ return true;
+
+ // The settings file is corrupt, so reinitialize it.
+ handle.reset();
+
+ // The settings failed to be read, so re-initialize them.
+ return RecoverSettings(kInvalidFileHandle, out_data);
+}
+
+Settings::ScopedLockedFileHandle Settings::OpenForWritingAndReadSettings(
+ Data* out_data) {
+ ScopedLockedFileHandle handle = OpenForReadingAndWriting();
+ if (!handle.is_valid())
+ return ScopedLockedFileHandle();
+
+ if (!ReadSettings(handle.get(), out_data)) {
+ if (!RecoverSettings(handle.get(), out_data))
+ return ScopedLockedFileHandle();
+ }
+
+ return handle.Pass();
+}
+
+bool Settings::ReadSettings(FileHandle handle, Data* out_data) {
+ if (LoggingSeekFile(handle, 0, SEEK_SET) != 0)
+ return false;
+
+ if (!LoggingReadFile(handle, out_data, sizeof(*out_data)))
+ return false;
+
+ if (out_data->magic != Data::kSettingsMagic) {
+ LOG(ERROR) << "Settings magic is not " << Data::kSettingsMagic;
+ return false;
+ }
+
+ if (out_data->version != Data::kSettingsVersion) {
+ LOG(ERROR) << "Settings version is not " << Data::kSettingsVersion;
+ return false;
+ }
+
+ return true;
+}
+
+bool Settings::WriteSettings(FileHandle handle, const Data& data) {
+ if (LoggingSeekFile(handle, 0, SEEK_SET) != 0)
+ return false;
+
+ if (!LoggingTruncateFile(handle))
+ return false;
+
+ return LoggingWriteFile(handle, &data, sizeof(Data));
+}
+
+bool Settings::RecoverSettings(FileHandle handle, Data* out_data) {
+ ScopedLockedFileHandle scoped_handle;
+ if (handle == kInvalidFileHandle) {
+ scoped_handle = OpenForReadingAndWriting();
+ handle = scoped_handle.get();
+
+ // Test if the file has already been recovered now that the exclusive lock
+ // is held.
+ if (ReadSettings(handle, out_data))
+ return true;
+ }
+
+ if (handle == kInvalidFileHandle) {
+ LOG(ERROR) << "Invalid file handle";
+ return false;
+ }
+
+ if (!InitializeSettings(handle))
+ return false;
+
+ return ReadSettings(handle, out_data);
+}
+
+bool Settings::InitializeSettings(FileHandle handle) {
+ Data settings;
+ if (!settings.client_id.InitializeWithNew())
+ return false;
+
+ return WriteSettings(handle, settings);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/client/settings.h b/chromium/third_party/crashpad/crashpad/client/settings.h
new file mode 100644
index 00000000000..61e18c4339e
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/settings.h
@@ -0,0 +1,165 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_CLIENT_SETTINGS_H_
+#define CRASHPAD_CLIENT_SETTINGS_H_
+
+#include <time.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/scoped_generic.h"
+#include "util/file/file_io.h"
+#include "util/misc/initialization_state_dcheck.h"
+#include "util/misc/uuid.h"
+
+namespace crashpad {
+
+namespace internal {
+
+struct ScopedLockedFileHandleTraits {
+ static FileHandle InvalidValue();
+ static void Free(FileHandle handle);
+};
+
+} // namespace internal
+
+//! \brief An interface for accessing and modifying the settings of a
+//! CrashReportDatabase.
+//!
+//! This class must not be instantiated directly, but rather an instance of it
+//! should be retrieved via CrashReportDatabase::GetSettings().
+class Settings {
+ public:
+ explicit Settings(const base::FilePath& file_path);
+ ~Settings();
+
+ bool Initialize();
+
+ //! \brief Retrieves the immutable identifier for this client, which is used
+ //! on a server to locate all crash reports from a specific Crashpad
+ //! database.
+ //!
+ //! This is automatically initialized when the database is created.
+ //!
+ //! \param[out] client_id The unique client identifier.
+ //!
+ //! \return On success, returns `true`, otherwise returns `false` with an
+ //! error logged.
+ bool GetClientID(UUID* client_id);
+
+ //! \brief Retrieves the user’s preference for submitting crash reports to a
+ //! collection server.
+ //!
+ //! The default value is `false`.
+ //!
+ //! \param[out] enabled Whether crash reports should be uploaded.
+ //!
+ //! \return On success, returns `true`, otherwise returns `false` with an
+ //! error logged.
+ bool GetUploadsEnabled(bool* enabled);
+
+ //! \brief Sets the user’s preference for submitting crash reports to a
+ //! collection server.
+ //!
+ //! \param[in] enabled Whether crash reports should be uploaded.
+ //!
+ //! \return On success, returns `true`, otherwise returns `false` with an
+ //! error logged.
+ bool SetUploadsEnabled(bool enabled);
+
+ //! \brief Retrieves the last time at which a report was attempted to be
+ //! uploaded.
+ //!
+ //! The default value is `0` if it has never been set before.
+ //!
+ //! \param[out] time The last time at which a report was uploaded.
+ //!
+ //! \return On success, returns `true`, otherwise returns `false` with an
+ //! error logged.
+ bool GetLastUploadAttemptTime(time_t* time);
+
+ //! \brief Sets the last time at which a report was attempted to be uploaded.
+ //!
+ //! This is only meant to be used internally by the CrashReportDatabase.
+ //!
+ //! \param[in] time The last time at which a report was uploaded.
+ //!
+ //! \return On success, returns `true`, otherwise returns `false` with an
+ //! error logged.
+ bool SetLastUploadAttemptTime(time_t time);
+
+ private:
+ struct Data;
+
+ // This must be constructed with MakeScopedLockedFileHandle(). It both unlocks
+ // and closes the file on destruction.
+ using ScopedLockedFileHandle =
+ base::ScopedGeneric<FileHandle,
+ internal::ScopedLockedFileHandleTraits>;
+ static ScopedLockedFileHandle MakeScopedLockedFileHandle(FileHandle file,
+ FileLocking locking);
+
+ // Opens the settings file for reading. On error, logs a message and returns
+ // the invalid handle.
+ ScopedLockedFileHandle OpenForReading();
+
+ // Opens the settings file for reading and writing. On error, logs a message
+ // and returns the invalid handle.
+ ScopedLockedFileHandle OpenForReadingAndWriting();
+
+ // Opens the settings file and reads the data. If that fails, an error will
+ // be logged and the settings will be recovered and re-initialized. If that
+ // also fails, returns false with additional log data from recovery.
+ bool OpenAndReadSettings(Data* out_data);
+
+ // Opens the settings file for writing and reads the data. If reading fails,
+ // recovery is attempted. Returns the opened file handle on success, or the
+ // invalid file handle on failure, with an error logged.
+ ScopedLockedFileHandle OpenForWritingAndReadSettings(Data* out_data);
+
+ // Reads the settings from |handle|. Logs an error and returns false on
+ // failure. This does not perform recovery.
+ bool ReadSettings(FileHandle handle, Data* out_data);
+
+ // Writes the settings to |handle|. Logs an error and returns false on
+ // failure. This does not perform recovery.
+ bool WriteSettings(FileHandle handle, const Data& data);
+
+ // Recovers the settings file by re-initializing the data. If |handle| is the
+ // invalid handle, this will open the file; if it is not, then it must be the
+ // result of OpenForReadingAndWriting(). If the invalid handle is passed, the
+ // caller must not be holding the handle. The new settings data are stored in
+ // |out_data|. Returns true on success and false on failure, with an error
+ // logged.
+ bool RecoverSettings(FileHandle handle, Data* out_data);
+
+ // Initializes a settings file and writes the data to |handle|. Returns true
+ // on success and false on failure, with an error logged.
+ bool InitializeSettings(FileHandle handle);
+
+ const base::FilePath& file_path() const { return file_path_; }
+
+ base::FilePath file_path_;
+
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(Settings);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_CLIENT_SETTINGS_H_
diff --git a/chromium/third_party/crashpad/crashpad/client/settings_test.cc b/chromium/third_party/crashpad/crashpad/client/settings_test.cc
new file mode 100644
index 00000000000..203bd215ae3
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/settings_test.cc
@@ -0,0 +1,181 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "client/settings.h"
+
+#include "gtest/gtest.h"
+#include "test/errors.h"
+#include "test/scoped_temp_dir.h"
+#include "util/file/file_io.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+class SettingsTest : public testing::Test {
+ public:
+ SettingsTest() : settings_(settings_path()) {}
+
+ base::FilePath settings_path() {
+ return temp_dir_.path().Append(FILE_PATH_LITERAL("settings"));
+ }
+
+ Settings* settings() { return &settings_; }
+
+ void InitializeBadFile() {
+ ScopedFileHandle handle(
+ LoggingOpenFileForWrite(settings_path(),
+ FileWriteMode::kTruncateOrCreate,
+ FilePermissions::kWorldReadable));
+ ASSERT_TRUE(handle.is_valid());
+
+ const char kBuf[] = "test bad file";
+ ASSERT_TRUE(LoggingWriteFile(handle.get(), kBuf, sizeof(kBuf)));
+ handle.reset();
+ }
+
+ protected:
+ // testing::Test:
+ void SetUp() override {
+ ASSERT_TRUE(settings()->Initialize());
+ }
+
+ private:
+ ScopedTempDir temp_dir_;
+ Settings settings_;
+
+ DISALLOW_COPY_AND_ASSIGN(SettingsTest);
+};
+
+TEST_F(SettingsTest, ClientID) {
+ UUID client_id;
+ EXPECT_TRUE(settings()->GetClientID(&client_id));
+ EXPECT_NE(UUID(), client_id);
+
+ Settings local_settings(settings_path());
+ EXPECT_TRUE(local_settings.Initialize());
+ UUID actual;
+ EXPECT_TRUE(local_settings.GetClientID(&actual));
+ EXPECT_EQ(client_id, actual);
+}
+
+TEST_F(SettingsTest, UploadsEnabled) {
+ bool enabled = true;
+ // Default value is false.
+ EXPECT_TRUE(settings()->GetUploadsEnabled(&enabled));
+ EXPECT_FALSE(enabled);
+
+ EXPECT_TRUE(settings()->SetUploadsEnabled(true));
+ EXPECT_TRUE(settings()->GetUploadsEnabled(&enabled));
+ EXPECT_TRUE(enabled);
+
+ Settings local_settings(settings_path());
+ EXPECT_TRUE(local_settings.Initialize());
+ enabled = false;
+ EXPECT_TRUE(local_settings.GetUploadsEnabled(&enabled));
+ EXPECT_TRUE(enabled);
+
+ EXPECT_TRUE(settings()->SetUploadsEnabled(false));
+ EXPECT_TRUE(settings()->GetUploadsEnabled(&enabled));
+ EXPECT_FALSE(enabled);
+
+ enabled = true;
+ EXPECT_TRUE(local_settings.GetUploadsEnabled(&enabled));
+ EXPECT_FALSE(enabled);
+}
+
+TEST_F(SettingsTest, LastUploadAttemptTime) {
+ time_t actual = -1;
+ EXPECT_TRUE(settings()->GetLastUploadAttemptTime(&actual));
+ // Default value is 0.
+ EXPECT_EQ(0, actual);
+
+ const time_t expected = time(nullptr);
+ EXPECT_TRUE(settings()->SetLastUploadAttemptTime(expected));
+ EXPECT_TRUE(settings()->GetLastUploadAttemptTime(&actual));
+ EXPECT_EQ(expected, actual);
+
+ Settings local_settings(settings_path());
+ EXPECT_TRUE(local_settings.Initialize());
+ actual = -1;
+ EXPECT_TRUE(local_settings.GetLastUploadAttemptTime(&actual));
+ EXPECT_EQ(expected, actual);
+}
+
+// The following tests write a corrupt settings file and test the recovery
+// operation.
+
+TEST_F(SettingsTest, BadFileOnInitialize) {
+ InitializeBadFile();
+
+ Settings settings(settings_path());
+ EXPECT_TRUE(settings.Initialize());
+}
+
+TEST_F(SettingsTest, BadFileOnGet) {
+ InitializeBadFile();
+
+ UUID client_id;
+ EXPECT_TRUE(settings()->GetClientID(&client_id));
+ EXPECT_NE(UUID(), client_id);
+
+ Settings local_settings(settings_path());
+ EXPECT_TRUE(local_settings.Initialize());
+ UUID actual;
+ EXPECT_TRUE(local_settings.GetClientID(&actual));
+ EXPECT_EQ(client_id, actual);
+}
+
+TEST_F(SettingsTest, BadFileOnSet) {
+ InitializeBadFile();
+
+ EXPECT_TRUE(settings()->SetUploadsEnabled(true));
+ bool enabled = false;
+ EXPECT_TRUE(settings()->GetUploadsEnabled(&enabled));
+ EXPECT_TRUE(enabled);
+}
+
+TEST_F(SettingsTest, UnlinkFile) {
+ UUID client_id;
+ EXPECT_TRUE(settings()->GetClientID(&client_id));
+ EXPECT_TRUE(settings()->SetUploadsEnabled(true));
+ EXPECT_TRUE(settings()->SetLastUploadAttemptTime(time(nullptr)));
+
+#if defined(OS_WIN)
+ EXPECT_EQ(0, _wunlink(settings_path().value().c_str()))
+ << ErrnoMessage("_wunlink");
+#else
+ EXPECT_EQ(0, unlink(settings_path().value().c_str()))
+ << ErrnoMessage("unlink");
+#endif
+
+ Settings local_settings(settings_path());
+ EXPECT_TRUE(local_settings.Initialize());
+ UUID new_client_id;
+ EXPECT_TRUE(local_settings.GetClientID(&new_client_id));
+ EXPECT_NE(client_id, new_client_id);
+
+ // Check that all values are reset.
+ bool enabled = true;
+ EXPECT_TRUE(local_settings.GetUploadsEnabled(&enabled));
+ EXPECT_FALSE(enabled);
+
+ time_t time = -1;
+ EXPECT_TRUE(local_settings.GetLastUploadAttemptTime(&time));
+ EXPECT_EQ(0, time);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/client/simple_string_dictionary.cc b/chromium/third_party/crashpad/crashpad/client/simple_string_dictionary.cc
new file mode 100644
index 00000000000..985b8938459
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/simple_string_dictionary.cc
@@ -0,0 +1,45 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "client/simple_string_dictionary.h"
+
+#include "util/stdlib/cxx.h"
+
+#if CXX_LIBRARY_VERSION >= 2011
+#include <type_traits>
+#endif
+
+namespace crashpad {
+namespace {
+
+using SimpleStringDictionaryForAssertion = TSimpleStringDictionary<1, 1, 1>;
+
+#if CXX_LIBRARY_VERSION >= 2011
+// In C++11, check that TSimpleStringDictionary has standard layout, which is
+// what is actually important.
+static_assert(
+ std::is_standard_layout<SimpleStringDictionaryForAssertion>::value,
+ "SimpleStringDictionary must be standard layout");
+#else
+// In C++98 (ISO 14882), section 9.5.1 says that a union cannot have a member
+// with a non-trivial ctor, copy ctor, dtor, or assignment operator. Use this
+// property to ensure that Entry remains POD. This doesn’t work for C++11
+// because the requirements for unions have been relaxed.
+union Compile_Assert {
+ SimpleStringDictionaryForAssertion::Entry Compile_Assert__entry_must_be_pod;
+};
+#endif
+
+} // namespace
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/client/simple_string_dictionary.h b/chromium/third_party/crashpad/crashpad/client/simple_string_dictionary.h
new file mode 100644
index 00000000000..73eba626ce1
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/simple_string_dictionary.h
@@ -0,0 +1,274 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_CLIENT_SIMPLE_STRING_DICTIONARY_H_
+#define CRASHPAD_CLIENT_SIMPLE_STRING_DICTIONARY_H_
+
+#include <string.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace crashpad {
+
+// Opaque type for the serialized representation of a TSimpleStringDictionary.
+// One is created in TSimpleStringDictionary::Serialize and can be deserialized
+// using one of the constructors.
+struct SerializedSimpleStringDictionary;
+
+//! \brief A map/dictionary collection implementation using a fixed amount of
+//! storage, so that it does not perform any dynamic allocations for its
+//! operations.
+//!
+//! The actual map storage (TSimpleStringDictionary::Entry) is guaranteed to be
+//! POD, so that it can be transmitted over various IPC mechanisms.
+//!
+//! The template parameters control the amount of storage used for the key,
+//! value, and map. The \a KeySize and \a ValueSize are measured in bytes, not
+//! glyphs, and include space for a trailing `NUL` byte. This gives space for
+//! `KeySize - 1` and `ValueSize - 1` characters in an entry. \a NumEntries is
+//! the total number of entries that will fit in the map.
+template <size_t KeySize = 256, size_t ValueSize = 256, size_t NumEntries = 64>
+class TSimpleStringDictionary {
+ public:
+ //! \brief Constant and publicly accessible versions of the template
+ //! parameters.
+ //! \{
+ static const size_t key_size = KeySize;
+ static const size_t value_size = ValueSize;
+ static const size_t num_entries = NumEntries;
+ //! \}
+
+ //! \brief A single entry in the map.
+ struct Entry {
+ //! \brief The entry’s key.
+ //!
+ //! If this is a 0-length `NUL`-terminated string, the entry is inactive.
+ char key[KeySize];
+
+ //! \brief The entry’s value.
+ char value[ValueSize];
+
+ //! \brief Returns the validity of the entry.
+ //!
+ //! If #key is an empty string, the entry is considered inactive, and this
+ //! method returns `false`. Otherwise, returns `true`.
+ bool is_active() const {
+ return key[0] != '\0';
+ }
+ };
+
+ //! \brief An iterator to traverse all of the active entries in a
+ //! TSimpleStringDictionary.
+ class Iterator {
+ public:
+ explicit Iterator(const TSimpleStringDictionary& map)
+ : map_(map),
+ current_(0) {
+ }
+
+ //! \brief Returns the next entry in the map, or `nullptr` if at the end of
+ //! the collection.
+ const Entry* Next() {
+ while (current_ < map_.num_entries) {
+ const Entry* entry = &map_.entries_[current_++];
+ if (entry->is_active()) {
+ return entry;
+ }
+ }
+ return nullptr;
+ }
+
+ private:
+ const TSimpleStringDictionary& map_;
+ size_t current_;
+
+ DISALLOW_COPY_AND_ASSIGN(Iterator);
+ };
+
+ TSimpleStringDictionary()
+ : entries_() {
+ }
+
+ TSimpleStringDictionary(const TSimpleStringDictionary& other) {
+ *this = other;
+ }
+
+ TSimpleStringDictionary& operator=(const TSimpleStringDictionary& other) {
+ memcpy(entries_, other.entries_, sizeof(entries_));
+ return *this;
+ }
+
+ //! \brief Constructs a map from its serialized form. \a map should be the out
+ //! parameter from Serialize(), and \a size should be its return value.
+ TSimpleStringDictionary(
+ const SerializedSimpleStringDictionary* map, size_t size) {
+ DCHECK_EQ(size, sizeof(entries_));
+ if (size == sizeof(entries_)) {
+ memcpy(entries_, map, size);
+ }
+ }
+
+ //! \brief Returns the number of active key/value pairs. The upper limit for
+ //! this is \a NumEntries.
+ size_t GetCount() const {
+ size_t count = 0;
+ for (size_t i = 0; i < num_entries; ++i) {
+ if (entries_[i].is_active()) {
+ ++count;
+ }
+ }
+ return count;
+ }
+
+ //! \brief Given \a key, returns its corresponding value.
+ //!
+ //! \param[in] key The key to look up. This must not be `nullptr`.
+ //!
+ //! \return The corresponding value for \a key, or if \a key is not found,
+ //! `nullptr`.
+ const char* GetValueForKey(const char* key) const {
+ DCHECK(key);
+ if (!key) {
+ return nullptr;
+ }
+
+ const Entry* entry = GetConstEntryForKey(key);
+ if (!entry) {
+ return nullptr;
+ }
+
+ return entry->value;
+ }
+
+ //! \brief Stores \a value into \a key, replacing the existing value if \a key
+ //! is already present.
+ //!
+ //! If \a key is not yet in the map and the map is already full (containing
+ //! \a NumEntries active entries), this operation silently fails.
+ //!
+ //! \param[in] key The key to store. This must not be `nullptr`.
+ //! \param[in] value The value to store. If `nullptr`, \a key is removed from
+ //! the map.
+ void SetKeyValue(const char* key, const char* value) {
+ if (!value) {
+ RemoveKey(key);
+ return;
+ }
+
+ DCHECK(key);
+ if (!key) {
+ return;
+ }
+
+ // |key| must not be an empty string.
+ DCHECK_NE(key[0], '\0');
+ if (key[0] == '\0') {
+ return;
+ }
+
+ Entry* entry = GetEntryForKey(key);
+
+ // If it does not yet exist, attempt to insert it.
+ if (!entry) {
+ for (size_t i = 0; i < num_entries; ++i) {
+ if (!entries_[i].is_active()) {
+ entry = &entries_[i];
+
+ strncpy(entry->key, key, key_size);
+ entry->key[key_size - 1] = '\0';
+
+ break;
+ }
+ }
+ }
+
+ // If the map is out of space, |entry| will be nullptr.
+ if (!entry) {
+ return;
+ }
+
+#ifndef NDEBUG
+ // Sanity check that the key only appears once.
+ int count = 0;
+ for (size_t i = 0; i < num_entries; ++i) {
+ if (strncmp(entries_[i].key, key, key_size) == 0) {
+ ++count;
+ }
+ }
+ DCHECK_EQ(count, 1);
+#endif
+
+ strncpy(entry->value, value, value_size);
+ entry->value[value_size - 1] = '\0';
+ }
+
+ //! \brief Removes \a key from the map.
+ //!
+ //! If \a key is not found, this is a no-op.
+ //!
+ //! \param[in] key The key of the entry to remove. This must not be `nullptr`.
+ void RemoveKey(const char* key) {
+ DCHECK(key);
+ if (!key) {
+ return;
+ }
+
+ Entry* entry = GetEntryForKey(key);
+ if (entry) {
+ entry->key[0] = '\0';
+ entry->value[0] = '\0';
+ }
+
+ DCHECK_EQ(GetEntryForKey(key), implicit_cast<Entry*>(nullptr));
+ }
+
+ //! \brief Returns a serialized form of the map.
+ //!
+ //! Places a serialized version of the map into \a map and returns the size in
+ //! bytes. Both \a map and the size should be passed to the deserializing
+ //! constructor. Note that the serialized \a map is scoped to the lifetime of
+ //! the non-serialized instance of this class. The \a map data can be copied
+ //! across IPC boundaries.
+ size_t Serialize(const SerializedSimpleStringDictionary** map) const {
+ *map = reinterpret_cast<const SerializedSimpleStringDictionary*>(entries_);
+ return sizeof(entries_);
+ }
+
+ private:
+ const Entry* GetConstEntryForKey(const char* key) const {
+ for (size_t i = 0; i < num_entries; ++i) {
+ if (strncmp(key, entries_[i].key, key_size) == 0) {
+ return &entries_[i];
+ }
+ }
+ return nullptr;
+ }
+
+ Entry* GetEntryForKey(const char* key) {
+ return const_cast<Entry*>(GetConstEntryForKey(key));
+ }
+
+ Entry entries_[NumEntries];
+};
+
+//! \brief A TSimpleStringDictionary with default template parameters.
+//!
+//! For historical reasons this specialized version is available with the same
+//! size factors as a previous implementation.
+using SimpleStringDictionary = TSimpleStringDictionary<256, 256, 64>;
+
+} // namespace crashpad
+
+#endif // CRASHPAD_CLIENT_SIMPLE_STRING_DICTIONARY_H_
diff --git a/chromium/third_party/crashpad/crashpad/client/simple_string_dictionary_test.cc b/chromium/third_party/crashpad/crashpad/client/simple_string_dictionary_test.cc
new file mode 100644
index 00000000000..f1e34013615
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/simple_string_dictionary_test.cc
@@ -0,0 +1,298 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "client/simple_string_dictionary.h"
+
+#include "base/logging.h"
+#include "gtest/gtest.h"
+#include "test/gtest_death_check.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(SimpleStringDictionary, Entry) {
+ using TestMap = TSimpleStringDictionary<5, 9, 15>;
+ TestMap map;
+
+ const TestMap::Entry* entry = TestMap::Iterator(map).Next();
+ EXPECT_FALSE(entry);
+
+ // Try setting a key/value and then verify.
+ map.SetKeyValue("key1", "value1");
+ entry = TestMap::Iterator(map).Next();
+ ASSERT_TRUE(entry);
+ EXPECT_STREQ(entry->key, "key1");
+ EXPECT_STREQ(entry->value, "value1");
+
+ // Try setting a new value.
+ map.SetKeyValue("key1", "value3");
+ EXPECT_STREQ(entry->value, "value3");
+
+ // Make sure the key didn't change.
+ EXPECT_STREQ(entry->key, "key1");
+
+ // Clear the entry and verify the key and value are empty strings.
+ map.RemoveKey("key1");
+ EXPECT_FALSE(entry->is_active());
+ EXPECT_EQ(strlen(entry->key), 0u);
+ EXPECT_EQ(strlen(entry->value), 0u);
+}
+
+TEST(SimpleStringDictionary, SimpleStringDictionary) {
+ // Make a new dictionary
+ SimpleStringDictionary dict;
+
+ // Set three distinct values on three keys
+ dict.SetKeyValue("key1", "value1");
+ dict.SetKeyValue("key2", "value2");
+ dict.SetKeyValue("key3", "value3");
+
+ EXPECT_NE(dict.GetValueForKey("key1"), "value1");
+ EXPECT_NE(dict.GetValueForKey("key2"), "value2");
+ EXPECT_NE(dict.GetValueForKey("key3"), "value3");
+ EXPECT_EQ(dict.GetCount(), 3u);
+ // try an unknown key
+ EXPECT_FALSE(dict.GetValueForKey("key4"));
+
+ // Remove a key
+ dict.RemoveKey("key3");
+
+ // Now make sure it's not there anymore
+ EXPECT_FALSE(dict.GetValueForKey("key3"));
+
+ // Remove by setting value to nullptr
+ dict.SetKeyValue("key2", nullptr);
+
+ // Now make sure it's not there anymore
+ EXPECT_FALSE(dict.GetValueForKey("key2"));
+}
+
+TEST(SimpleStringDictionary, CopyAndAssign) {
+ TSimpleStringDictionary<10, 10, 10> map;
+ map.SetKeyValue("one", "a");
+ map.SetKeyValue("two", "b");
+ map.SetKeyValue("three", "c");
+ map.RemoveKey("two");
+ EXPECT_EQ(2u, map.GetCount());
+
+ // Test copy.
+ TSimpleStringDictionary<10, 10, 10> map_copy(map);
+ EXPECT_EQ(2u, map_copy.GetCount());
+ EXPECT_STREQ("a", map_copy.GetValueForKey("one"));
+ EXPECT_STREQ("c", map_copy.GetValueForKey("three"));
+ map_copy.SetKeyValue("four", "d");
+ EXPECT_STREQ("d", map_copy.GetValueForKey("four"));
+ EXPECT_FALSE(map.GetValueForKey("four"));
+
+ // Test assign.
+ TSimpleStringDictionary<10, 10, 10> map_assign;
+ map_assign = map;
+ EXPECT_EQ(2u, map_assign.GetCount());
+ EXPECT_STREQ("a", map_assign.GetValueForKey("one"));
+ EXPECT_STREQ("c", map_assign.GetValueForKey("three"));
+ map_assign.SetKeyValue("four", "d");
+ EXPECT_STREQ("d", map_assign.GetValueForKey("four"));
+ EXPECT_FALSE(map.GetValueForKey("four"));
+
+ map.RemoveKey("one");
+ EXPECT_FALSE(map.GetValueForKey("one"));
+ EXPECT_STREQ("a", map_copy.GetValueForKey("one"));
+ EXPECT_STREQ("a", map_assign.GetValueForKey("one"));
+}
+
+// Add a bunch of values to the dictionary, remove some entries in the middle,
+// and then add more.
+TEST(SimpleStringDictionary, Iterator) {
+ SimpleStringDictionary* dict = new SimpleStringDictionary;
+ ASSERT_TRUE(dict);
+
+ char key[SimpleStringDictionary::key_size];
+ char value[SimpleStringDictionary::value_size];
+
+ const int kDictionaryCapacity = SimpleStringDictionary::num_entries;
+ const int kPartitionIndex = kDictionaryCapacity - 5;
+
+ // We assume at least this size in the tests below
+ ASSERT_GE(kDictionaryCapacity, 64);
+
+ // We'll keep track of the number of key/value pairs we think should be in the
+ // dictionary
+ int expectedDictionarySize = 0;
+
+ // Set a bunch of key/value pairs like key0/value0, key1/value1, ...
+ for (int i = 0; i < kPartitionIndex; ++i) {
+ sprintf(key, "key%d", i);
+ sprintf(value, "value%d", i);
+ dict->SetKeyValue(key, value);
+ }
+ expectedDictionarySize = kPartitionIndex;
+
+ // set a couple of the keys twice (with the same value) - should be nop
+ dict->SetKeyValue("key2", "value2");
+ dict->SetKeyValue("key4", "value4");
+ dict->SetKeyValue("key15", "value15");
+
+ // Remove some random elements in the middle
+ dict->RemoveKey("key7");
+ dict->RemoveKey("key18");
+ dict->RemoveKey("key23");
+ dict->RemoveKey("key31");
+ expectedDictionarySize -= 4; // we just removed four key/value pairs
+
+ // Set some more key/value pairs like key59/value59, key60/value60, ...
+ for (int i = kPartitionIndex; i < kDictionaryCapacity; ++i) {
+ sprintf(key, "key%d", i);
+ sprintf(value, "value%d", i);
+ dict->SetKeyValue(key, value);
+ }
+ expectedDictionarySize += kDictionaryCapacity - kPartitionIndex;
+
+ // Now create an iterator on the dictionary
+ SimpleStringDictionary::Iterator iter(*dict);
+
+ // We then verify that it iterates through exactly the number of key/value
+ // pairs we expect, and that they match one-for-one with what we would expect.
+ // The ordering of the iteration does not matter...
+
+ // used to keep track of number of occurrences found for key/value pairs
+ int count[kDictionaryCapacity];
+ memset(count, 0, sizeof(count));
+
+ int totalCount = 0;
+
+ const SimpleStringDictionary::Entry* entry;
+ while ((entry = iter.Next())) {
+ totalCount++;
+
+ // Extract keyNumber from a string of the form key<keyNumber>
+ int keyNumber;
+ sscanf(entry->key, "key%d", &keyNumber);
+
+ // Extract valueNumber from a string of the form value<valueNumber>
+ int valueNumber;
+ sscanf(entry->value, "value%d", &valueNumber);
+
+ // The value number should equal the key number since that's how we set them
+ EXPECT_EQ(keyNumber, valueNumber);
+
+ // Key and value numbers should be in proper range: 0 <= keyNumber <
+ // kDictionaryCapacity
+ bool isKeyInGoodRange = (keyNumber >= 0 && keyNumber < kDictionaryCapacity);
+ bool isValueInGoodRange =
+ (valueNumber >= 0 && valueNumber < kDictionaryCapacity);
+ EXPECT_TRUE(isKeyInGoodRange);
+ EXPECT_TRUE(isValueInGoodRange);
+
+ if (isKeyInGoodRange && isValueInGoodRange) {
+ ++count[keyNumber];
+ }
+ }
+
+ // Make sure each of the key/value pairs showed up exactly one time, except
+ // for the ones which we removed.
+ for (size_t i = 0; i < kDictionaryCapacity; ++i) {
+ // Skip over key7, key18, key23, and key31, since we removed them
+ if (!(i == 7 || i == 18 || i == 23 || i == 31)) {
+ EXPECT_EQ(count[i], 1);
+ }
+ }
+
+ // Make sure the number of iterations matches the expected dictionary size.
+ EXPECT_EQ(totalCount, expectedDictionarySize);
+}
+
+TEST(SimpleStringDictionary, AddRemove) {
+ TSimpleStringDictionary<5, 7, 6> map;
+ map.SetKeyValue("rob", "ert");
+ map.SetKeyValue("mike", "pink");
+ map.SetKeyValue("mark", "allays");
+
+ EXPECT_EQ(3u, map.GetCount());
+ EXPECT_STREQ("ert", map.GetValueForKey("rob"));
+ EXPECT_STREQ("pink", map.GetValueForKey("mike"));
+ EXPECT_STREQ("allays", map.GetValueForKey("mark"));
+
+ map.RemoveKey("mike");
+
+ EXPECT_EQ(2u, map.GetCount());
+ EXPECT_FALSE(map.GetValueForKey("mike"));
+
+ map.SetKeyValue("mark", "mal");
+ EXPECT_EQ(2u, map.GetCount());
+ EXPECT_STREQ("mal", map.GetValueForKey("mark"));
+
+ map.RemoveKey("mark");
+ EXPECT_EQ(1u, map.GetCount());
+ EXPECT_FALSE(map.GetValueForKey("mark"));
+}
+
+TEST(SimpleStringDictionary, Serialize) {
+ using TestMap = TSimpleStringDictionary<4, 5, 7>;
+ TestMap map;
+ map.SetKeyValue("one", "abc");
+ map.SetKeyValue("two", "def");
+ map.SetKeyValue("tre", "hig");
+
+ EXPECT_STREQ("abc", map.GetValueForKey("one"));
+ EXPECT_STREQ("def", map.GetValueForKey("two"));
+ EXPECT_STREQ("hig", map.GetValueForKey("tre"));
+
+ const SerializedSimpleStringDictionary* serialized;
+ size_t size = map.Serialize(&serialized);
+
+ SerializedSimpleStringDictionary* serialized_copy =
+ reinterpret_cast<SerializedSimpleStringDictionary*>(malloc(size));
+ ASSERT_TRUE(serialized_copy);
+ memcpy(serialized_copy, serialized, size);
+
+ TestMap deserialized(serialized_copy, size);
+ free(serialized_copy);
+
+ EXPECT_EQ(3u, deserialized.GetCount());
+ EXPECT_STREQ("abc", deserialized.GetValueForKey("one"));
+ EXPECT_STREQ("def", deserialized.GetValueForKey("two"));
+ EXPECT_STREQ("hig", deserialized.GetValueForKey("tre"));
+}
+
+// Running out of space shouldn't crash.
+TEST(SimpleStringDictionary, OutOfSpace) {
+ TSimpleStringDictionary<3, 2, 2> map;
+ map.SetKeyValue("a", "1");
+ map.SetKeyValue("b", "2");
+ map.SetKeyValue("c", "3");
+ EXPECT_EQ(2u, map.GetCount());
+ EXPECT_FALSE(map.GetValueForKey("c"));
+}
+
+#if DCHECK_IS_ON
+
+TEST(SimpleStringDictionaryDeathTest, NullKey) {
+ TSimpleStringDictionary<4, 6, 6> map;
+ ASSERT_DEATH_CHECK(map.SetKeyValue(nullptr, "hello"), "key");
+
+ map.SetKeyValue("hi", "there");
+ ASSERT_DEATH_CHECK(map.GetValueForKey(nullptr), "key");
+ EXPECT_STREQ("there", map.GetValueForKey("hi"));
+
+ ASSERT_DEATH_CHECK(map.GetValueForKey(nullptr), "key");
+ map.RemoveKey("hi");
+ EXPECT_EQ(0u, map.GetCount());
+}
+
+#endif
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/client/simulate_crash.h b/chromium/third_party/crashpad/crashpad/client/simulate_crash.h
new file mode 100644
index 00000000000..aa4625e2f28
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/simulate_crash.h
@@ -0,0 +1,24 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_CLIENT_SIMULATE_CRASH_H_
+#define CRASHPAD_CLIENT_SIMULATE_CRASH_H_
+
+#include "build/build_config.h"
+
+#if defined(OS_MACOSX)
+#include "client/simulate_crash_mac.h"
+#endif
+
+#endif // CRASHPAD_CLIENT_SIMULATE_CRASH_H_
diff --git a/chromium/third_party/crashpad/crashpad/client/simulate_crash_mac.cc b/chromium/third_party/crashpad/crashpad/client/simulate_crash_mac.cc
new file mode 100644
index 00000000000..cdcf662d68e
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/simulate_crash_mac.cc
@@ -0,0 +1,241 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "client/simulate_crash_mac.h"
+
+#include <string.h>
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/strings/stringprintf.h"
+#include "build/build_config.h"
+#include "util/mach/exc_client_variants.h"
+#include "util/mach/exception_behaviors.h"
+#include "util/mach/exception_ports.h"
+#include "util/mach/mach_extensions.h"
+
+namespace crashpad {
+
+namespace {
+
+//! \brief Sends an exception message to an exception port in accordance with
+//! the behavior and thread state flavor it’s registered to receive.
+//!
+//! \param[in] thread, task, exception, code, code_count These parameters will
+//! be passed to the exception handler as appropriate.
+//! \param[in] cpu_context The value to use for the thread state, if \a behavior
+//! indicates that the handler should receive a thread state and if the
+//! supplied thread state matches or can be converted to \a flavor. If \a
+//! behavior requires a thread state but this argument cannot be converted
+//! to match \a flavor, `thread_get_state()` will be called to obtain a
+//! suitable thread state value.
+//! \param[in] handler The Mach exception handler to deliver the exception to.
+//! \param[in] set_state If `true` and \a behavior indicates that the handler
+//! should receive and return a thread state, a new thread state will be set
+//! by `thread_set_state()` upon successful completion of the exception
+//! handler. If `false`, this will be suppressed, even when \a behavior
+//! indicates that the handler receives and returns a thread state.
+//!
+//! \return `true` if the exception was delivered to the handler and the handler
+//! indicated success. `false` otherwise, with a warning message logged.
+bool DeliverException(thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const mach_exception_data_t code,
+ mach_msg_type_number_t code_count,
+ const NativeCPUContext* cpu_context,
+ const ExceptionPorts::ExceptionHandler& handler,
+ bool set_state) {
+ kern_return_t kr;
+
+ bool handler_wants_state = ExceptionBehaviorHasState(handler.behavior);
+ if (!handler_wants_state) {
+ // Regardless of the passed-in value of |set_state|, if the handler won’t be
+ // dealing with any state at all, no state should be set.
+ set_state = false;
+ }
+
+ // old_state is only used if the context already captured doesn’t match (or
+ // can’t be converted to) what’s registered for the handler.
+ thread_state_data_t old_state;
+
+ thread_state_flavor_t flavor = handler.flavor;
+ ConstThreadState state;
+ mach_msg_type_number_t state_count;
+ switch (flavor) {
+#if defined(ARCH_CPU_X86_FAMILY)
+ case x86_THREAD_STATE:
+ state = reinterpret_cast<ConstThreadState>(cpu_context);
+ state_count = x86_THREAD_STATE_COUNT;
+ break;
+#if defined(ARCH_CPU_X86)
+ case x86_THREAD_STATE32:
+ state = reinterpret_cast<ConstThreadState>(&cpu_context->uts.ts32);
+ state_count = cpu_context->tsh.count;
+ break;
+#elif defined(ARCH_CPU_X86_64)
+ case x86_THREAD_STATE64:
+ state = reinterpret_cast<ConstThreadState>(&cpu_context->uts.ts64);
+ state_count = cpu_context->tsh.count;
+ break;
+#endif
+#else
+#error Port to your CPU architecture
+#endif
+
+ case THREAD_STATE_NONE:
+ // This is only acceptable if the handler doesn’t have one of the “state”
+ // behaviors. Otherwise, if the kernel were attempting to send an
+ // exception message to this port, it would call thread_getstatus() (known
+ // outside the kernel as thread_get_state()) which would fail because
+ // THREAD_STATE_NONE is not a valid state to get. See 10.9.5
+ // xnu-2422.115.4/osfmk/kern/exception.c exception_deliver() and
+ // xnu-2422.115.4/osfmk/i386/pcb.c machine_thread_get_state().
+ if (!handler_wants_state) {
+ state = nullptr;
+ state_count = 0;
+ break;
+ }
+
+ LOG(WARNING) << "exception handler has unexpected state flavor" << flavor;
+ return false;
+
+ default:
+ if (!handler_wants_state) {
+ // Don’t bother getting any thread state if the handler’s not actually
+ // going to use it.
+ state = nullptr;
+ state_count = 0;
+ } else {
+ state = old_state;
+ state_count = THREAD_STATE_MAX;
+ kr = thread_get_state(thread, flavor, old_state, &state_count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(WARNING, kr) << "thread_get_state";
+ return false;
+ }
+ }
+ break;
+ }
+
+ // new_state is supposed to be an out parameter only, but in case the handler
+ // doesn't touch it, make sure it's initialized to a valid thread state.
+ // Otherwise, the thread_set_state() call below would set a garbage thread
+ // state.
+ thread_state_data_t new_state;
+ size_t state_size =
+ sizeof(natural_t) *
+ std::min(state_count, implicit_cast<unsigned int>(THREAD_STATE_MAX));
+ memcpy(new_state, state, state_size);
+ mach_msg_type_number_t new_state_count = THREAD_STATE_MAX;
+
+ kr = UniversalExceptionRaise(handler.behavior,
+ handler.port,
+ thread,
+ task,
+ exception,
+ code,
+ code_count,
+ &flavor,
+ state,
+ state_count,
+ new_state,
+ &new_state_count);
+
+ // The kernel treats a return value of MACH_RCV_PORT_DIED as successful,
+ // although it will not set a new thread state in that case. See 10.9.5
+ // xnu-2422.115.4/osfmk/kern/exception.c exception_deliver(), and the more
+ // elaborate comment at util/mach/exc_server_variants.h
+ // ExcServerSuccessfulReturnValue(). Duplicate that behavior.
+ bool success = kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED;
+ MACH_LOG_IF(WARNING, !success, kr) << "UniversalExceptionRaise";
+
+ if (kr == KERN_SUCCESS && set_state) {
+ kr = thread_set_state(thread, flavor, new_state, new_state_count);
+ MACH_LOG_IF(WARNING, kr != KERN_SUCCESS, kr) << "thread_set_state";
+ }
+
+ return success;
+}
+
+} // namespace
+
+void SimulateCrash(const NativeCPUContext* cpu_context) {
+#if defined(ARCH_CPU_X86)
+ DCHECK_EQ(cpu_context->tsh.flavor,
+ implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE32));
+ DCHECK_EQ(implicit_cast<mach_msg_type_number_t>(cpu_context->tsh.count),
+ x86_THREAD_STATE32_COUNT);
+#elif defined(ARCH_CPU_X86_64)
+ DCHECK_EQ(cpu_context->tsh.flavor,
+ implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE64));
+ DCHECK_EQ(implicit_cast<mach_msg_type_number_t>(cpu_context->tsh.count),
+ x86_THREAD_STATE64_COUNT);
+#endif
+
+ base::mac::ScopedMachSendRight thread(mach_thread_self());
+ exception_type_t exception = kMachExceptionSimulated;
+ mach_exception_data_type_t codes[] = {0, 0};
+ mach_msg_type_number_t code_count = arraysize(codes);
+
+ // Look up the handler for EXC_CRASH exceptions in the same way that the
+ // kernel would: try a thread handler, then a task handler, and finally a host
+ // handler. 10.9.5 xnu-2422.115.4/osfmk/kern/exception.c exception_triage().
+ const ExceptionPorts::TargetType kTargetTypes[] = {
+ ExceptionPorts::kTargetTypeThread,
+ ExceptionPorts::kTargetTypeTask,
+
+ // This is not expected to succeed, because mach_host_self() doesn’t
+ // return the host_priv port to non-root users, and this is the port
+ // that’s required for host_get_exception_ports().
+ //
+ // See 10.9.5 xnu-2422.115.4/bsd/kern/kern_prot.c set_security_token(),
+ // xnu-2422.115.4/osfmk/kern/task.c host_security_set_task_token(), and
+ // xnu-2422.115.4/osfmk/kern/ipc_host.c host_get_exception_ports().
+ ExceptionPorts::kTargetTypeHost,
+ };
+
+ bool success = false;
+
+ for (size_t target_type_index = 0;
+ !success && target_type_index < arraysize(kTargetTypes);
+ ++target_type_index) {
+ std::vector<ExceptionPorts::ExceptionHandler> handlers;
+ ExceptionPorts exception_ports(kTargetTypes[target_type_index],
+ MACH_PORT_NULL);
+ if (exception_ports.GetExceptionPorts(EXC_MASK_CRASH, &handlers)) {
+ DCHECK_LE(handlers.size(), 1u);
+ if (handlers.size() == 1) {
+ DCHECK(handlers[0].mask & EXC_MASK_CRASH);
+ success = DeliverException(thread,
+ mach_task_self(),
+ exception,
+ codes,
+ code_count,
+ cpu_context,
+ handlers[0],
+ false);
+ }
+ }
+ }
+
+ LOG_IF(WARNING, !success)
+ << "SimulateCrash did not find an appropriate exception handler";
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/client/simulate_crash_mac.h b/chromium/third_party/crashpad/crashpad/client/simulate_crash_mac.h
new file mode 100644
index 00000000000..7c11cd21e39
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/simulate_crash_mac.h
@@ -0,0 +1,60 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_CLIENT_SIMULATE_CRASH_MAC_H_
+#define CRASHPAD_CLIENT_SIMULATE_CRASH_MAC_H_
+
+#include <mach/mach.h>
+
+#include "client/capture_context_mac.h"
+
+//! \file
+
+namespace crashpad {
+
+//! \brief Simulates a exception without crashing.
+//!
+//! This function searches for an `EXC_CRASH` handler in the same manner that
+//! the kernel does, and sends it an exception message to that handler in the
+//! format that the handler expects, considering the behavior and thread state
+//! flavor that are registered for it. The exception sent to the handler will be
+//! ::kMachExceptionSimulated, not `EXC_CRASH`.
+//!
+//! Typically, the CRASHPAD_SIMULATE_CRASH() macro will be used in preference to
+//! this function, because it combines the context-capture operation with the
+//! raising of a simulated exception.
+//!
+//! This function returns normally after the exception message is processed. If
+//! no valid handler was found, or no handler processed the exception
+//! successfully, a warning will be logged, but these conditions are not
+//! considered fatal.
+//!
+//! \param[in] cpu_context The thread state to pass to the exception handler as
+//! the exception context, provided that it is compatible with the thread
+//! state flavor that the exception handler accepts. If it is not
+//! compatible, the correct thread state for the handler will be obtained by
+//! calling `thread_get_state()`.
+void SimulateCrash(const NativeCPUContext* cpu_context);
+
+} // namespace crashpad
+
+//! \brief Captures the CPU context and simulates an exception without crashing.
+#define CRASHPAD_SIMULATE_CRASH() \
+ do { \
+ crashpad::NativeCPUContext cpu_context; \
+ crashpad::CaptureContext(&cpu_context); \
+ crashpad::SimulateCrash(&cpu_context); \
+ } while (false)
+
+#endif // CRASHPAD_CLIENT_SIMULATE_CRASH_MAC_H_
diff --git a/chromium/third_party/crashpad/crashpad/client/simulate_crash_mac_test.cc b/chromium/third_party/crashpad/crashpad/client/simulate_crash_mac_test.cc
new file mode 100644
index 00000000000..e585838ae34
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/simulate_crash_mac_test.cc
@@ -0,0 +1,386 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "client/simulate_crash.h"
+
+#include <mach/mach.h>
+#include <string.h>
+
+#include "base/basictypes.h"
+#include "base/strings/stringprintf.h"
+#include "build/build_config.h"
+#include "gtest/gtest.h"
+#include "test/mac/mach_errors.h"
+#include "test/mac/mach_multiprocess.h"
+#include "util/mach/exc_server_variants.h"
+#include "util/mach/exception_behaviors.h"
+#include "util/mach/exception_ports.h"
+#include "util/mach/mach_extensions.h"
+#include "util/mach/mach_message.h"
+#include "util/mach/mach_message_server.h"
+#include "util/mach/symbolic_constants_mach.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+class TestSimulateCrashMac final : public MachMultiprocess,
+ public UniversalMachExcServer::Interface {
+ public:
+ // Defines which targets the child should set an EXC_CRASH exception handler
+ // for.
+ enum ExceptionPortsTarget {
+ // The child should clear its EXC_CRASH handler for both its task and thread
+ // targets. SimulateCrash() will attempt to deliver the exception to the
+ // host target, which will fail if not running as root. In any case, the
+ // parent should not expect to receive any exception message from the child.
+ kExceptionPortsTargetNone = 0,
+
+ // The child will set an EXC_CRASH handler for its task target, and clear it
+ // for its thread target. The parent runs an exception server to receive
+ // the child’s simulated crash message.
+ kExceptionPortsTargetTask,
+
+ // The child will set an EXC_CRASH handler for its thread target, and clear
+ // it for its task target. The parent runs an exception server to receive
+ // the child’s simulated crash message.
+ kExceptionPortsTargetThread,
+
+ // The child sets an EXC_CRASH handler for both its task and thread targets.
+ // The parent runs an exception server to receive the message expected to be
+ // delivered to the thread target, but returns an error code. The child will
+ // then fall back to trying the server registered for the task target,
+ // sending a second message to the parent. The server in the parent will
+ // handle this one successfully.
+ kExceptionPortsTargetBoth,
+ };
+
+ TestSimulateCrashMac(ExceptionPortsTarget target,
+ exception_behavior_t behavior,
+ thread_state_flavor_t flavor)
+ : MachMultiprocess(),
+ UniversalMachExcServer::Interface(),
+ target_(target),
+ behavior_(behavior),
+ flavor_(flavor),
+ succeed_(true) {
+ }
+
+ ~TestSimulateCrashMac() {}
+
+ // UniversalMachExcServer::Interface:
+ kern_return_t CatchMachException(exception_behavior_t behavior,
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_complex_request) override {
+ *destroy_complex_request = true;
+
+ // Check the entire exception message, because most or all of it was
+ // generated by SimulateCrash() instead of the kernel.
+
+ EXPECT_EQ(behavior_, behavior);
+ EXPECT_EQ(LocalPort(), exception_port);
+ if (ExceptionBehaviorHasIdentity(behavior)) {
+ EXPECT_NE(THREAD_NULL, thread);
+ EXPECT_EQ(ChildTask(), task);
+ } else {
+ EXPECT_EQ(THREAD_NULL, thread);
+ EXPECT_EQ(TASK_NULL, task);
+ }
+ EXPECT_EQ(kMachExceptionSimulated, exception);
+ EXPECT_EQ(2u, code_count);
+ if (code_count >= 1) {
+ EXPECT_EQ(0, code[0]);
+ }
+ if (code_count >= 2) {
+ EXPECT_EQ(0, code[1]);
+ }
+ if (!ExceptionBehaviorHasState(behavior)) {
+ EXPECT_EQ(THREAD_STATE_NONE, *flavor);
+ } else {
+ EXPECT_EQ(flavor_, *flavor);
+ switch (*flavor) {
+#if defined(ARCH_CPU_X86_FAMILY)
+ case x86_THREAD_STATE: {
+ EXPECT_EQ(x86_THREAD_STATE_COUNT, old_state_count);
+ const x86_thread_state* state =
+ reinterpret_cast<const x86_thread_state*>(old_state);
+ switch (state->tsh.flavor) {
+ case x86_THREAD_STATE32:
+ EXPECT_EQ(implicit_cast<int>(x86_THREAD_STATE32_COUNT),
+ state->tsh.count);
+ break;
+ case x86_THREAD_STATE64:
+ EXPECT_EQ(implicit_cast<int>(x86_THREAD_STATE64_COUNT),
+ state->tsh.count);
+ break;
+ default:
+ ADD_FAILURE() << "unexpected tsh.flavor " << state->tsh.flavor;
+ break;
+ }
+ break;
+ }
+ case x86_FLOAT_STATE: {
+ EXPECT_EQ(x86_FLOAT_STATE_COUNT, old_state_count);
+ const x86_float_state* state =
+ reinterpret_cast<const x86_float_state*>(old_state);
+ switch (state->fsh.flavor) {
+ case x86_FLOAT_STATE32:
+ EXPECT_EQ(implicit_cast<int>(x86_FLOAT_STATE32_COUNT),
+ state->fsh.count);
+ break;
+ case x86_FLOAT_STATE64:
+ EXPECT_EQ(implicit_cast<int>(x86_FLOAT_STATE64_COUNT),
+ state->fsh.count);
+ break;
+ default:
+ ADD_FAILURE() << "unexpected fsh.flavor " << state->fsh.flavor;
+ break;
+ }
+ break;
+ }
+ case x86_DEBUG_STATE: {
+ EXPECT_EQ(x86_DEBUG_STATE_COUNT, old_state_count);
+ const x86_debug_state* state =
+ reinterpret_cast<const x86_debug_state*>(old_state);
+ switch (state->dsh.flavor) {
+ case x86_DEBUG_STATE32:
+ EXPECT_EQ(implicit_cast<int>(x86_DEBUG_STATE32_COUNT),
+ state->dsh.count);
+ break;
+ case x86_DEBUG_STATE64:
+ EXPECT_EQ(implicit_cast<int>(x86_DEBUG_STATE64_COUNT),
+ state->dsh.count);
+ break;
+ default:
+ ADD_FAILURE() << "unexpected dsh.flavor " << state->dsh.flavor;
+ break;
+ }
+ break;
+ }
+ case x86_THREAD_STATE32:
+ EXPECT_EQ(x86_THREAD_STATE32_COUNT, old_state_count);
+ break;
+ case x86_FLOAT_STATE32:
+ EXPECT_EQ(x86_FLOAT_STATE32_COUNT, old_state_count);
+ break;
+ case x86_DEBUG_STATE32:
+ EXPECT_EQ(x86_DEBUG_STATE32_COUNT, old_state_count);
+ break;
+ case x86_THREAD_STATE64:
+ EXPECT_EQ(x86_THREAD_STATE64_COUNT, old_state_count);
+ break;
+ case x86_FLOAT_STATE64:
+ EXPECT_EQ(x86_FLOAT_STATE64_COUNT, old_state_count);
+ break;
+ case x86_DEBUG_STATE64:
+ EXPECT_EQ(x86_DEBUG_STATE64_COUNT, old_state_count);
+ break;
+#else
+#error Port to your CPU architecture
+#endif
+ default:
+ ADD_FAILURE() << "unexpected flavor " << *flavor;
+ break;
+ }
+
+ // Attempt to set a garbage thread state, which would cause the child to
+ // crash inside SimulateCrash() if it actually succeeded. This tests that
+ // SimulateCrash() ignores new_state instead of attempting to set the
+ // state as the kernel would do. This operates in conjunction with the
+ // |true| argument to ExcServerSuccessfulReturnValue() below.
+ *new_state_count = old_state_count;
+ size_t new_state_size = sizeof(natural_t) * old_state_count;
+ memset(new_state, 0xa5, new_state_size);
+ }
+
+ if (!succeed_) {
+ // The client has registered EXC_CRASH handlers for both its thread and
+ // task targets, and sent a simulated exception message to its
+ // thread-level EXC_CRASH handler. To test that it will fall back to
+ // trying the task-level EXC_CRASH handler, return a failure code, which
+ // should cause SimulateCrash() to try the next target.
+ EXPECT_EQ(kExceptionPortsTargetBoth, target_);
+ return KERN_ABORTED;
+ }
+
+ ExcServerCopyState(
+ behavior, old_state, old_state_count, new_state, new_state_count);
+
+ return ExcServerSuccessfulReturnValue(behavior, true);
+ }
+
+ private:
+ // MachMultiprocess:
+
+ void MachMultiprocessParent() override {
+ if (target_ == kExceptionPortsTargetNone) {
+ // The child does not have any EXC_CRASH handlers registered for its
+ // thread or task targets, so no exception message is expected to be
+ // generated. Don’t run the server at all.
+ return;
+ }
+
+ UniversalMachExcServer universal_mach_exc_server(this);
+
+ mach_msg_return_t mr;
+ if (target_ == kExceptionPortsTargetBoth) {
+ // The client has registered EXC_CRASH handlers for both its thread and
+ // task targets. Run a server that will return a failure code when the
+ // exception message is sent to the thread target, which will cause the
+ // client to fall back to the task target and send another message.
+ succeed_ = false;
+ mr = MachMessageServer::Run(&universal_mach_exc_server,
+ LocalPort(),
+ MACH_MSG_OPTION_NONE,
+ MachMessageServer::kOneShot,
+ MachMessageServer::kReceiveLargeError,
+ kMachMessageTimeoutWaitIndefinitely);
+ EXPECT_EQ(MACH_MSG_SUCCESS, mr)
+ << MachErrorMessage(mr, "MachMessageServer::Run");
+ }
+
+ succeed_ = true;
+ mr = MachMessageServer::Run(&universal_mach_exc_server,
+ LocalPort(),
+ MACH_MSG_OPTION_NONE,
+ MachMessageServer::kOneShot,
+ MachMessageServer::kReceiveLargeError,
+ kMachMessageTimeoutWaitIndefinitely);
+ EXPECT_EQ(MACH_MSG_SUCCESS, mr)
+ << MachErrorMessage(mr, "MachMessageServer::Run");
+ }
+
+ void MachMultiprocessChild() override {
+ bool task_valid = target_ == kExceptionPortsTargetTask ||
+ target_ == kExceptionPortsTargetBoth;
+ ExceptionPorts task_exception_ports(ExceptionPorts::kTargetTypeTask,
+ TASK_NULL);
+ ASSERT_TRUE(task_exception_ports.SetExceptionPort(
+ EXC_MASK_CRASH,
+ task_valid ? RemotePort() : MACH_PORT_NULL,
+ behavior_,
+ flavor_));
+
+ bool thread_valid = target_ == kExceptionPortsTargetThread ||
+ target_ == kExceptionPortsTargetBoth;
+ ExceptionPorts thread_exception_ports(ExceptionPorts::kTargetTypeThread,
+ THREAD_NULL);
+ ASSERT_TRUE(thread_exception_ports.SetExceptionPort(
+ EXC_MASK_CRASH,
+ thread_valid ? RemotePort() : MACH_PORT_NULL,
+ behavior_,
+ flavor_));
+
+ CRASHPAD_SIMULATE_CRASH();
+ }
+
+ ExceptionPortsTarget target_;
+ exception_behavior_t behavior_;
+ thread_state_flavor_t flavor_;
+ bool succeed_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestSimulateCrashMac);
+};
+
+TEST(SimulateCrash, SimulateCrash) {
+ const TestSimulateCrashMac::ExceptionPortsTarget kTargets[] = {
+ TestSimulateCrashMac::kExceptionPortsTargetNone,
+ TestSimulateCrashMac::kExceptionPortsTargetTask,
+ TestSimulateCrashMac::kExceptionPortsTargetThread,
+ TestSimulateCrashMac::kExceptionPortsTargetBoth,
+ };
+
+ const exception_behavior_t kBehaviors[] = {
+ EXCEPTION_DEFAULT,
+ EXCEPTION_STATE,
+ EXCEPTION_STATE_IDENTITY,
+ EXCEPTION_DEFAULT | kMachExceptionCodes,
+ EXCEPTION_STATE | kMachExceptionCodes,
+ EXCEPTION_STATE_IDENTITY | kMachExceptionCodes,
+ };
+
+ const thread_state_flavor_t kFlavors[] = {
+#if defined(ARCH_CPU_X86_FAMILY)
+ x86_THREAD_STATE,
+ x86_FLOAT_STATE,
+ x86_DEBUG_STATE,
+#if defined(ARCH_CPU_X86)
+ x86_THREAD_STATE32,
+ x86_FLOAT_STATE32,
+ x86_DEBUG_STATE32,
+#elif defined(ARCH_CPU_X86_64)
+ x86_THREAD_STATE64,
+ x86_FLOAT_STATE64,
+ x86_DEBUG_STATE64,
+#endif
+#else
+#error Port to your CPU architecture
+#endif
+ };
+
+ for (size_t target_index = 0;
+ target_index < arraysize(kTargets);
+ ++target_index) {
+ TestSimulateCrashMac::ExceptionPortsTarget target = kTargets[target_index];
+ SCOPED_TRACE(base::StringPrintf(
+ "target_index %zu, target %d", target_index, target));
+
+ for (size_t behavior_index = 0;
+ behavior_index < arraysize(kBehaviors);
+ ++behavior_index) {
+ exception_behavior_t behavior = kBehaviors[behavior_index];
+ SCOPED_TRACE(base::StringPrintf(
+ "behavior_index %zu, behavior %s",
+ behavior_index,
+ ExceptionBehaviorToString(behavior, kUseFullName | kUnknownIsNumeric)
+ .c_str()));
+
+ if (!ExceptionBehaviorHasState(behavior)) {
+ TestSimulateCrashMac test_simulate_crash_mac(
+ target, behavior, THREAD_STATE_NONE);
+ test_simulate_crash_mac.Run();
+ } else {
+ for (size_t flavor_index = 0;
+ flavor_index < arraysize(kFlavors);
+ ++flavor_index) {
+ thread_state_flavor_t flavor = kFlavors[flavor_index];
+ SCOPED_TRACE(base::StringPrintf(
+ "flavor_index %zu, flavor %s",
+ flavor_index,
+ ThreadStateFlavorToString(
+ flavor, kUseFullName | kUnknownIsNumeric).c_str()));
+
+ TestSimulateCrashMac test_simulate_crash_mac(
+ target, behavior, flavor);
+ test_simulate_crash_mac.Run();
+ }
+ }
+ }
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/codereview.settings b/chromium/third_party/crashpad/crashpad/codereview.settings
new file mode 100644
index 00000000000..c3673e8d658
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/codereview.settings
@@ -0,0 +1,18 @@
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+CODE_REVIEW_SERVER: codereview.chromium.org
+CC_LIST: crashpad-dev@chromium.org
+VIEW_VC: https://chromium.googlesource.com/crashpad/crashpad/+/
+PROJECT: crashpad
diff --git a/chromium/third_party/crashpad/crashpad/compat/compat.gyp b/chromium/third_party/crashpad/crashpad/compat/compat.gyp
new file mode 100644
index 00000000000..75f404973bc
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/compat.gyp
@@ -0,0 +1,83 @@
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'includes': [
+ '../build/crashpad.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'crashpad_compat',
+ 'type': 'static_library',
+ 'sources': [
+ 'mac/AvailabilityMacros.h',
+ 'mac/kern/exc_resource.h'
+ 'mac/mach/mach.h',
+ 'mac/mach-o/getsect.cc',
+ 'mac/mach-o/getsect.h',
+ 'mac/mach-o/loader.h',
+ 'mac/sys/resource.h',
+ 'non_mac/mach/mach.h',
+ 'non_win/dbghelp.h',
+ 'non_win/minwinbase.h',
+ 'non_win/timezoneapi.h',
+ 'non_win/verrsrc.h',
+ 'non_win/windows.h',
+ 'non_win/winnt.h',
+ 'win/getopt.h',
+ 'win/sys/types.h',
+ 'win/time.cc',
+ 'win/time.h',
+ 'win/winnt.h',
+ ],
+ 'conditions': [
+ ['OS=="mac"', {
+ 'dependencies': [
+ '../third_party/apple_cctools/apple_cctools.gyp:apple_cctools',
+ ],
+ 'include_dirs': [
+ 'mac',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ 'mac',
+ ],
+ },
+ }],
+ ['OS=="win"', {
+ 'include_dirs': [
+ 'win',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ 'win',
+ ],
+ },
+ 'dependencies': [
+ '../third_party/getopt/getopt.gyp:getopt',
+ ],
+ }, {
+ 'include_dirs': [
+ 'non_win',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ 'non_win',
+ ],
+ },
+ }],
+ ],
+ },
+ ],
+}
diff --git a/chromium/third_party/crashpad/crashpad/compat/mac/AvailabilityMacros.h b/chromium/third_party/crashpad/crashpad/compat/mac/AvailabilityMacros.h
new file mode 100644
index 00000000000..c8e16d74ef7
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/mac/AvailabilityMacros.h
@@ -0,0 +1,44 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_COMPAT_MAC_AVAILABILITYMACROS_H_
+#define CRASHPAD_COMPAT_MAC_AVAILABILITYMACROS_H_
+
+#include_next <AvailabilityMacros.h>
+
+// 10.7 SDK
+
+#ifndef MAC_OS_X_VERSION_10_7
+#define MAC_OS_X_VERSION_10_7 1070
+#endif
+
+// 10.8 SDK
+
+#ifndef MAC_OS_X_VERSION_10_8
+#define MAC_OS_X_VERSION_10_8 1080
+#endif
+
+// 10.9 SDK
+
+#ifndef MAC_OS_X_VERSION_10_9
+#define MAC_OS_X_VERSION_10_9 1090
+#endif
+
+// 10.10 SDK
+
+#ifndef MAC_OS_X_VERSION_10_10
+#define MAC_OS_X_VERSION_10_10 101000
+#endif
+
+#endif // CRASHPAD_COMPAT_MAC_AVAILABILITYMACROS_H_
diff --git a/chromium/third_party/crashpad/crashpad/compat/mac/kern/exc_resource.h b/chromium/third_party/crashpad/crashpad/compat/mac/kern/exc_resource.h
new file mode 100644
index 00000000000..30e9b855115
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/mac/kern/exc_resource.h
@@ -0,0 +1,62 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_COMPAT_MAC_KERN_EXC_RESOURCE_H_
+#define CRASHPAD_COMPAT_MAC_KERN_EXC_RESOURCE_H_
+
+#if __has_include_next(<kern/exc_resource.h>)
+#include_next <kern/exc_resource.h>
+#endif
+
+// 10.9 SDK
+
+#ifndef EXC_RESOURCE_DECODE_RESOURCE_TYPE
+#define EXC_RESOURCE_DECODE_RESOURCE_TYPE(code) (((code) >> 61) & 0x7ull)
+#endif
+
+#ifndef EXC_RESOURCE_DECODE_FLAVOR
+#define EXC_RESOURCE_DECODE_FLAVOR(code) (((code) >> 58) & 0x7ull)
+#endif
+
+#ifndef RESOURCE_TYPE_CPU
+#define RESOURCE_TYPE_CPU 1
+#endif
+
+#ifndef RESOURCE_TYPE_WAKEUPS
+#define RESOURCE_TYPE_WAKEUPS 2
+#endif
+
+#ifndef RESOURCE_TYPE_MEMORY
+#define RESOURCE_TYPE_MEMORY 3
+#endif
+
+#ifndef FLAVOR_CPU_MONITOR
+#define FLAVOR_CPU_MONITOR 1
+#endif
+
+#ifndef FLAVOR_WAKEUPS_MONITOR
+#define FLAVOR_WAKEUPS_MONITOR 1
+#endif
+
+#ifndef FLAVOR_HIGH_WATERMARK
+#define FLAVOR_HIGH_WATERMARK 1
+#endif
+
+// 10.10 SDK
+
+#ifndef FLAVOR_CPU_MONITOR_FATAL
+#define FLAVOR_CPU_MONITOR_FATAL 2
+#endif
+
+#endif // CRASHPAD_COMPAT_MAC_KERN_EXC_RESOURCE_H_
diff --git a/chromium/third_party/crashpad/crashpad/compat/mac/mach-o/getsect.cc b/chromium/third_party/crashpad/crashpad/compat/mac/mach-o/getsect.cc
new file mode 100644
index 00000000000..71d3066c677
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/mac/mach-o/getsect.cc
@@ -0,0 +1,107 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <mach-o/getsect.h>
+
+// This is only necessary when building code that might run on systems earlier
+// than 10.7. When building for 10.7 or later, getsectiondata() and
+// getsegmentdata() are always present in libmacho and made available through
+// libSystem. When building for earlier systems, custom definitions of
+// these functions are needed.
+//
+// This file checks the deployment target instead of the SDK. The deployment
+// target is correct because it identifies the earliest possible system that
+// the code being compiled is expected to run on.
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
+
+#include <dlfcn.h>
+#include <stddef.h>
+
+#include "third_party/apple_cctools/cctools/include/mach-o/getsect.h"
+
+namespace {
+
+// Returns a dlopen() handle to the same library that provides the
+// getsectbyname() function. getsectbyname() is always present in libmacho.
+// getsectiondata() and getsegmentdata() are not always present, but when they
+// are, they’re in the same library as getsectbyname(). If the library cannot
+// be found or a handle to it cannot be returned, returns nullptr.
+void* SystemLibMachOHandle() {
+ Dl_info info;
+ if (!dladdr(reinterpret_cast<void*>(getsectbyname), &info)) {
+ return nullptr;
+ }
+ return dlopen(info.dli_fname, RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
+}
+
+// Returns a function pointer to a function in libmacho based on a lookup of
+// that function by symbol name. Returns nullptr if libmacho cannot be found or
+// opened, or if the named symbol cannot be found in libmacho.
+void* LookUpSystemLibMachOSymbol(const char* symbol) {
+ static void* dl_handle = SystemLibMachOHandle();
+ if (!dl_handle) {
+ return nullptr;
+ }
+ return dlsym(dl_handle, symbol);
+}
+
+#ifndef __LP64__
+using MachHeader = mach_header;
+#else
+using MachHeader = mach_header_64;
+#endif
+
+using GetSectionDataType =
+ uint8_t*(*)(const MachHeader*, const char*, const char*, unsigned long*);
+using GetSegmentDataType =
+ uint8_t*(*)(const MachHeader*, const char*, unsigned long*);
+
+} // namespace
+
+extern "C" {
+
+// These implementations look up their functions in libmacho at run time. If
+// the system libmacho provides these functions as it normally does on Mac OS X
+// 10.7 and later, the system’s versions are used directly. Otherwise, the
+// versions in third_party/apple_cctools are used, which are actually just
+// copies of the system’s functions.
+
+uint8_t* getsectiondata(const MachHeader* mhp,
+ const char* segname,
+ const char* sectname,
+ unsigned long* size) {
+ static GetSectionDataType system_getsectiondata =
+ reinterpret_cast<GetSectionDataType>(
+ LookUpSystemLibMachOSymbol("getsectiondata"));
+ if (system_getsectiondata) {
+ return system_getsectiondata(mhp, segname, sectname, size);
+ }
+ return crashpad_getsectiondata(mhp, segname, sectname, size);
+}
+
+uint8_t* getsegmentdata(
+ const MachHeader* mhp, const char* segname, unsigned long* size) {
+ static GetSegmentDataType system_getsegmentdata =
+ reinterpret_cast<GetSegmentDataType>(
+ LookUpSystemLibMachOSymbol("getsegmentdata"));
+ if (system_getsegmentdata) {
+ return system_getsegmentdata(mhp, segname, size);
+ }
+ return crashpad_getsegmentdata(mhp, segname, size);
+}
+
+} // extern "C"
+
+#endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
diff --git a/chromium/third_party/crashpad/crashpad/compat/mac/mach-o/getsect.h b/chromium/third_party/crashpad/crashpad/compat/mac/mach-o/getsect.h
new file mode 100644
index 00000000000..c9169e16499
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/mac/mach-o/getsect.h
@@ -0,0 +1,69 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_COMPAT_MAC_MACH_O_GETSECT_H_
+#define CRASHPAD_COMPAT_MAC_MACH_O_GETSECT_H_
+
+#include_next <mach-o/getsect.h>
+
+#include <AvailabilityMacros.h>
+
+// This file checks the SDK instead of the deployment target. The SDK is correct
+// because this file is concerned with providing compile-time declarations,
+// which are either present in a specific SDK version or not.
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
+
+#include <mach-o/loader.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Don’t use a type alias to account for the mach_header/mach_header_64
+// difference between the 32-bit and 64-bit versions of getsectiondata() and
+// getsegmentdata(). This file should be faithfully equivalent to the native
+// SDK, and adding type aliases here would pollute the namespace in a way that
+// the native SDK does not.
+
+#if !defined(__LP64__)
+
+uint8_t* getsectiondata(const struct mach_header* mhp,
+ const char* segname,
+ const char* sectname,
+ unsigned long* size);
+
+uint8_t* getsegmentdata(
+ const struct mach_header* mhp, const char* segname, unsigned long* size);
+
+#else
+
+uint8_t* getsectiondata(const struct mach_header_64* mhp,
+ const char* segname,
+ const char* sectname,
+ unsigned long* size);
+
+uint8_t* getsegmentdata(
+ const struct mach_header_64* mhp, const char* segname, unsigned long* size);
+
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
+
+#endif // CRASHPAD_COMPAT_MAC_MACH_O_GETSECT_H_
diff --git a/chromium/third_party/crashpad/crashpad/compat/mac/mach-o/loader.h b/chromium/third_party/crashpad/crashpad/compat/mac/mach-o/loader.h
new file mode 100644
index 00000000000..95a735747f6
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/mac/mach-o/loader.h
@@ -0,0 +1,32 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_COMPAT_MAC_MACH_O_LOADER_H_
+#define CRASHPAD_COMPAT_MAC_MACH_O_LOADER_H_
+
+#include_next <mach-o/loader.h>
+
+// 10.7 SDK
+
+#ifndef S_THREAD_LOCAL_ZEROFILL
+#define S_THREAD_LOCAL_ZEROFILL 0x12
+#endif
+
+// 10.8 SDK
+
+#ifndef LC_SOURCE_VERSION
+#define LC_SOURCE_VERSION 0x2a
+#endif
+
+#endif // CRASHPAD_COMPAT_MAC_MACH_O_LOADER_H_
diff --git a/chromium/third_party/crashpad/crashpad/compat/mac/mach/mach.h b/chromium/third_party/crashpad/crashpad/compat/mac/mach/mach.h
new file mode 100644
index 00000000000..0cf86d05f5d
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/mac/mach/mach.h
@@ -0,0 +1,97 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_COMPAT_MAC_MACH_MACH_H_
+#define CRASHPAD_COMPAT_MAC_MACH_MACH_H_
+
+#include_next <mach/mach.h>
+
+// <mach/exception_types.h>
+
+// 10.8 SDK
+
+#ifndef EXC_RESOURCE
+#define EXC_RESOURCE 11
+#endif
+
+#ifndef EXC_MASK_RESOURCE
+#define EXC_MASK_RESOURCE (1 << EXC_RESOURCE)
+#endif
+
+// 10.9 SDK
+
+#ifndef EXC_GUARD
+#define EXC_GUARD 12
+#endif
+
+#ifndef EXC_MASK_GUARD
+#define EXC_MASK_GUARD (1 << EXC_GUARD)
+#endif
+
+// Don’t expose EXC_MASK_ALL or EXC_MASK_VALID at all, because their definitions
+// vary with SDK, and older kernels will reject values that they don’t
+// understand. Instead, use crashpad::ExcMaskAll(), which computes the correct
+// value of EXC_MASK_ALL for the running system.
+#undef EXC_MASK_ALL
+#undef EXC_MASK_VALID
+
+#if defined(__i386__) || defined(__x86_64__)
+
+// <mach/i386/exception.h>
+
+// 10.9 SDK
+
+#if EXC_TYPES_COUNT > 13 // Definition varies with SDK
+#error Update this file for new exception types
+#elif EXC_TYPES_COUNT != 13
+#undef EXC_TYPES_COUNT
+#define EXC_TYPES_COUNT 13
+#endif
+
+// <mach/i386/thread_status.h>
+
+// 10.6 SDK
+//
+// Earlier versions of this SDK didn’t have AVX definitions. They didn’t appear
+// until the version of the 10.6 SDK that shipped with Xcode 4.2, although
+// versions of this SDK appeared with Xcode releases as early as Xcode 3.2.
+// Similarly, the kernel didn’t handle AVX state until Mac OS X 10.6.8
+// (xnu-1504.15.3) and presumably the hardware-specific versions of Mac OS X
+// 10.6.7 intended to run on processors with AVX.
+
+#ifndef x86_AVX_STATE32
+#define x86_AVX_STATE32 16
+#endif
+
+#ifndef x86_AVX_STATE64
+#define x86_AVX_STATE64 17
+#endif
+
+// 10.8 SDK
+
+#ifndef x86_AVX_STATE
+#define x86_AVX_STATE 18
+#endif
+
+#endif // defined(__i386__) || defined(__x86_64__)
+
+// <mach/thread_status.h>
+
+// 10.8 SDK
+
+#ifndef THREAD_STATE_FLAVOR_LIST_10_9
+#define THREAD_STATE_FLAVOR_LIST_10_9 129
+#endif
+
+#endif // CRASHPAD_COMPAT_MAC_MACH_MACH_H_
diff --git a/chromium/third_party/crashpad/crashpad/compat/mac/sys/resource.h b/chromium/third_party/crashpad/crashpad/compat/mac/sys/resource.h
new file mode 100644
index 00000000000..0697e169b58
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/mac/sys/resource.h
@@ -0,0 +1,26 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_COMPAT_MAC_SYS_RESOURCE_H_
+#define CRASHPAD_COMPAT_MAC_SYS_RESOURCE_H_
+
+#include_next <sys/resource.h>
+
+// 10.9 SDK
+
+#ifndef WAKEMON_MAKE_FATAL
+#define WAKEMON_MAKE_FATAL 0x10
+#endif
+
+#endif // CRASHPAD_COMPAT_MAC_SYS_RESOURCE_H_
diff --git a/chromium/third_party/crashpad/crashpad/compat/non_mac/mach/mach.h b/chromium/third_party/crashpad/crashpad/compat/non_mac/mach/mach.h
new file mode 100644
index 00000000000..d7cc4905e6b
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/non_mac/mach/mach.h
@@ -0,0 +1,41 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_COMPAT_NON_MAC_MACH_MACH_H_
+#define CRASHPAD_COMPAT_NON_MAC_MACH_MACH_H_
+
+//! \file
+
+// <mach/exception_types.h>
+
+//! \anchor EXC_x
+//! \name EXC_*
+//!
+//! \brief Mach exception type definitions.
+//! \{
+#define EXC_BAD_ACCESS 1
+#define EXC_BAD_INSTRUCTION 2
+#define EXC_ARITHMETIC 3
+#define EXC_EMULATION 4
+#define EXC_SOFTWARE 5
+#define EXC_BREAKPOINT 6
+#define EXC_SYSCALL 7
+#define EXC_MACH_SYSCALL 8
+#define EXC_RPC_ALERT 9
+#define EXC_CRASH 10
+#define EXC_RESOURCE 11
+#define EXC_GUARD 12
+//! \}
+
+#endif // CRASHPAD_COMPAT_NON_MAC_MACH_MACH_H_
diff --git a/chromium/third_party/crashpad/crashpad/compat/non_win/dbghelp.h b/chromium/third_party/crashpad/crashpad/compat/non_win/dbghelp.h
new file mode 100644
index 00000000000..3be6e5af549
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/non_win/dbghelp.h
@@ -0,0 +1,853 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_COMPAT_NON_WIN_DBGHELP_H_
+#define CRASHPAD_COMPAT_NON_WIN_DBGHELP_H_
+
+#include <stdint.h>
+
+#include "base/strings/string16.h"
+#include "compat/non_win/timezoneapi.h"
+#include "compat/non_win/verrsrc.h"
+#include "compat/non_win/winnt.h"
+
+//! \file
+
+//! \brief The magic number for a minidump file, stored in
+//! MINIDUMP_HEADER::Signature.
+//!
+//! A hex dump of a little-endian minidump file will begin with the string
+//! “MDMP”.
+#define MINIDUMP_SIGNATURE ('PMDM') // 0x4d444d50
+
+//! \brief The version of a minidump file, stored in MINIDUMP_HEADER::Version.
+#define MINIDUMP_VERSION (42899)
+
+//! \brief An offset within a minidump file, relative to the start of its
+//! MINIDUMP_HEADER.
+//!
+//! RVA stands for “relative virtual address”. Within a minidump file, RVAs are
+//! used as pointers to link structures together.
+//!
+//! \sa MINIDUMP_LOCATION_DESCRIPTOR
+typedef uint32_t RVA;
+
+//! \brief A pointer to a structure or union within a minidump file.
+struct __attribute__((packed, aligned(4))) MINIDUMP_LOCATION_DESCRIPTOR {
+ //! \brief The size of the referenced structure or union, in bytes.
+ uint32_t DataSize;
+
+ //! \brief The relative virtual address of the structure or union within the
+ //! minidump file.
+ RVA Rva;
+};
+
+//! \brief A pointer to a snapshot of a region of memory contained within a
+//! minidump file.
+//!
+//! \sa MINIDUMP_MEMORY_LIST
+struct __attribute__((packed, aligned(4))) MINIDUMP_MEMORY_DESCRIPTOR {
+ //! \brief The base address of the memory region in the address space of the
+ //! process that the minidump file contains a snapshot of.
+ uint64_t StartOfMemoryRange;
+
+ //! \brief The contents of the memory region.
+ MINIDUMP_LOCATION_DESCRIPTOR Memory;
+};
+
+//! \brief The top-level structure identifying a minidump file.
+//!
+//! This structure contains a pointer to the stream directory, a second-level
+//! structure which in turn contains pointers to third-level structures
+//! (“streams”) containing the data within the minidump file. This structure
+//! also contains the minidump file’s magic numbers, and other bookkeeping data.
+//!
+//! This structure must be present at the beginning of a minidump file (at ::RVA
+//! 0).
+struct __attribute__((packed, aligned(4))) MINIDUMP_HEADER {
+ //! \brief The minidump file format magic number, ::MINIDUMP_SIGNATURE.
+ uint32_t Signature;
+
+ //! \brief The minidump file format version number, ::MINIDUMP_VERSION.
+ uint32_t Version;
+
+ //! \brief The number of MINIDUMP_DIRECTORY elements present in the directory
+ //! referenced by #StreamDirectoryRva.
+ uint32_t NumberOfStreams;
+
+ //! \brief A pointer to an array of MINIDUMP_DIRECTORY structures that
+ //! identify all of the streams within this minidump file. The array has
+ //! #NumberOfStreams elements present.
+ RVA StreamDirectoryRva;
+
+ //! \brief The minidump file’s checksum. This can be `0`, and in practice, `0`
+ //! is the only value that has ever been seen in this field.
+ uint32_t CheckSum;
+
+ //! \brief The time that the minidump file was generated, in `time_t` format,
+ //! the number of seconds since the POSIX epoch.
+ uint32_t TimeDateStamp;
+
+ //! \brief A bitfield containing members of ::MINIDUMP_TYPE, describing the
+ //! types of data carried within this minidump file.
+ uint64_t Flags;
+};
+
+//! \brief A pointer to a stream within a minidump file.
+//!
+//! Each stream present in a minidump file will have a corresponding
+//! MINIDUMP_DIRECTORY entry in the stream directory referenced by
+//! MINIDUMP_HEADER::StreamDirectoryRva.
+struct __attribute__((packed, aligned(4))) MINIDUMP_DIRECTORY {
+ //! \brief The type of stream referenced, a value of ::MINIDUMP_STREAM_TYPE.
+ uint32_t StreamType;
+
+ //! \brief A pointer to the stream data within the minidump file.
+ MINIDUMP_LOCATION_DESCRIPTOR Location;
+};
+
+//! \brief A variable-length UTF-16-encoded string carried within a minidump
+//! file.
+//!
+//! The UTF-16 string is stored as UTF-16LE or UTF-16BE according to the byte
+//! ordering of the minidump file itself.
+//!
+//! \sa crashpad::MinidumpUTF8String
+struct __attribute__((packed, aligned(4))) MINIDUMP_STRING {
+ //! \brief The length of the #Buffer field in bytes, not including the `NUL`
+ //! terminator.
+ //!
+ //! \note This field is interpreted as a byte count, not a count of UTF-16
+ //! code units or Unicode code points.
+ uint32_t Length;
+
+ //! \brief The string, encoded in UTF-16, and terminated with a UTF-16 `NUL`
+ //! code unit (two `NUL` bytes).
+ base::char16 Buffer[0];
+};
+
+//! \brief Minidump stream type values for MINIDUMP_DIRECTORY::StreamType. Each
+//! stream structure has a corresponding stream type value to identify it.
+//!
+//! \sa crashpad::MinidumpStreamType
+enum MINIDUMP_STREAM_TYPE {
+ //! \brief The stream type for MINIDUMP_THREAD_LIST.
+ ThreadListStream = 3,
+
+ //! \brief The stream type for MINIDUMP_MODULE_LIST.
+ ModuleListStream = 4,
+
+ //! \brief The stream type for MINIDUMP_MEMORY_LIST.
+ MemoryListStream = 5,
+
+ //! \brief The stream type for MINIDUMP_EXCEPTION_STREAM.
+ ExceptionStream = 6,
+
+ //! \brief The stream type for MINIDUMP_SYSTEM_INFO.
+ SystemInfoStream = 7,
+
+ //! \brief The stream type for MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2,
+ //! MINIDUMP_MISC_INFO_3, and MINIDUMP_MISC_INFO_4.
+ //!
+ //! More recent versions of this stream are supersets of earlier versions.
+ //!
+ //! The exact version of the stream that is present is implied by the stream’s
+ //! size. Furthermore, this stream contains a field,
+ //! MINIDUMP_MISC_INFO::Flags1, that indicates which data is present and
+ //! valid.
+ MiscInfoStream = 15,
+};
+
+//! \brief Information about the CPU (or CPUs) that ran the process that the
+//! minidump file contains a snapshot of.
+//!
+//! This union only appears as MINIDUMP_SYSTEM_INFO::Cpu. Its interpretation is
+//! controlled by MINIDUMP_SYSTEM_INFO::ProcessorArchitecture.
+union __attribute__((packed, aligned(4))) CPU_INFORMATION {
+ //! \brief Information about 32-bit x86 CPUs, or x86_64 CPUs when running
+ //! 32-bit x86 processes.
+ struct __attribute__((packed, aligned(4))) {
+ //! \brief The CPU’s vendor identification string as encoded in `cpuid 0`
+ //! `ebx`, `edx`, and `ecx`, represented as it appears in these
+ //! registers.
+ //!
+ //! For Intel CPUs, `[0]` will encode “Genu”, `[1]` will encode “ineI”, and
+ //! `[2]` will encode “ntel”, for a vendor ID string “GenuineIntel”.
+ //!
+ //! \note The Windows documentation incorrectly states that these fields are
+ //! to be interpreted as `cpuid 0` `eax`, `ebx`, and `ecx`.
+ uint32_t VendorId[3];
+
+ //! \brief Family, model, and stepping ID values as encoded in `cpuid 1`
+ //! `eax`.
+ uint32_t VersionInformation;
+
+ //! \brief A bitfield containing supported CPU capabilities as encoded in
+ //! `cpuid 1` `edx`.
+ uint32_t FeatureInformation;
+
+ //! \brief A bitfield containing supported CPU capabalities as encoded in
+ //! `cpuid 0x80000001` `edx`.
+ //!
+ //! This field is only valid if #VendorId identifies the CPU vendor as
+ //! “AuthenticAMD”.
+ uint32_t AMDExtendedCpuFeatures;
+ } X86CpuInfo;
+
+ //! \brief Information about non-x86 CPUs, and x86_64 CPUs when not running
+ //! 32-bit x86 processes.
+ struct __attribute__((packed, aligned(4))) {
+ //! \brief Bitfields containing supported CPU capabilities as identified by
+ //! bits corresponding to \ref PF_x "PF_*" values passed to
+ //! `IsProcessorFeaturePresent()`.
+ uint64_t ProcessorFeatures[2];
+ } OtherCpuInfo;
+};
+
+//! \brief Information about the system that hosted the process that the
+//! minidump file contains a snapshot of.
+struct __attribute__((packed, aligned(4))) MINIDUMP_SYSTEM_INFO {
+ // The next 4 fields are from the SYSTEM_INFO structure returned by
+ // GetSystemInfo().
+
+ //! \brief The system’s CPU architecture. This may be a \ref
+ //! PROCESSOR_ARCHITECTURE_x "PROCESSOR_ARCHITECTURE_*" value, or a member
+ //! of crashpad::MinidumpCPUArchitecture.
+ //!
+ //! In some cases, a system may be able to run processes of multiple specific
+ //! architecture types. For example, systems based on 64-bit architectures
+ //! such as x86_64 are often able to run 32-bit code of another architecture
+ //! in the same family, such as 32-bit x86. On these systems, this field will
+ //! identify the architecture of the process that the minidump file contains a
+ //! snapshot of.
+ uint16_t ProcessorArchitecture;
+
+ //! \brief General CPU version information.
+ //!
+ //! The precise interpretation of this field is specific to each CPU
+ //! architecture. For x86-family CPUs (including x86_64 and 32-bit x86), this
+ //! field contains the CPU family ID value from `cpuid 1` `eax`, adjusted to
+ //! take the extended family ID into account.
+ uint16_t ProcessorLevel;
+
+ //! \brief Specific CPU version information.
+ //!
+ //! The precise interpretation of this field is specific to each CPU
+ //! architecture. For x86-family CPUs (including x86_64 and 32-bit x86), this
+ //! field contains values obtained from `cpuid 1` `eax`: the high byte
+ //! contains the CPU model ID value adjusted to take the extended model ID
+ //! into account, and the low byte contains the CPU stepping ID value.
+ uint16_t ProcessorRevision;
+
+ //! \brief The total number of CPUs present in the system.
+ uint8_t NumberOfProcessors;
+
+ // The next 7 fields are from the OSVERSIONINFOEX structure returned by
+ // GetVersionEx().
+
+ //! \brief The system’s operating system type, which distinguishes between
+ //! “desktop” or “workstation” systems and “server” systems. This may be a
+ //! \ref VER_NT_x "VER_NT_*" value, or a member of
+ //! crashpad::MinidumpOSType.
+ uint8_t ProductType;
+
+ //! \brief The system’s operating system version number’s first (major)
+ //! component.
+ //!
+ //! - For Windows 7 (NT 6.1) SP1, version 6.1.7601, this would be `6`.
+ //! - For Mac OS X 10.9.2, this would be `10`.
+ uint32_t MajorVersion;
+
+ //! \brief The system’s operating system version number’s second (minor)
+ //! component.
+ //!
+ //! - For Windows 7 (NT 6.1) SP1, version 6.1.7601, this would be `1`.
+ //! - For Mac OS X 10.9.2, this would be `9`.
+ uint32_t MinorVersion;
+
+ //! \brief The system’s operating system version number’s third (build or
+ //! patch) component.
+ //!
+ //! - For Windows 7 (NT 6.1) SP1, version 6.1.7601, this would be `7601`.
+ //! - For Mac OS X 10.9.2, this would be `2`.
+ uint32_t BuildNumber;
+
+ //! \brief The system’s operating system family. This may be a \ref
+ //! VER_PLATFORM_x "VER_PLATFORM_*" value, or a member of
+ //! crashpad::MinidumpOS.
+ uint32_t PlatformId;
+
+ //! \brief ::RVA of a MINIDUMP_STRING containing operating system-specific
+ //! version information.
+ //!
+ //! This field further identifies an operating system version beyond its
+ //! version number fields. Historically, “CSD” stands for “corrective service
+ //! diskette.”
+ //!
+ //! - On Windows, this is the name of the installed operating system service
+ //! pack, such as “Service Pack 1”. If no service pack is installed, this
+ //! field references an empty string.
+ //! - On Mac OS X, this is the operating system build number from `sw_vers
+ //! -buildVersion`. For Mac OS X 10.9.2 on most hardware types, this would
+ //! be `13C64`.
+ //! - On Linux and other Unix-like systems, this is the kernel version from
+ //! `uname -srvm`, possibly with additional information appended. On
+ //! Android, the `ro.build.fingerprint` system property is appended.
+ RVA CSDVersionRva;
+
+ //! \brief A bitfield identifying products installed on the system. This is
+ //! composed of \ref VER_SUITE_x "VER_SUITE_*" values.
+ //!
+ //! This field is Windows-specific, and has no meaning on other operating
+ //! systems.
+ uint16_t SuiteMask;
+
+ uint16_t Reserved2;
+
+ //! \brief Information about the system’s CPUs.
+ //!
+ //! This field is a union. Which of its members should be expressed is
+ //! controlled by the #ProcessorArchitecture field. If it is set to
+ //! crashpad::kMinidumpCPUArchitectureX86, the CPU_INFORMATION::X86CpuInfo
+ //! field is expressed. Otherwise, the CPU_INFORMATION::OtherCpuInfo field is
+ //! expressed.
+ //!
+ //! \note Older Breakpad implementations produce minidump files that express
+ //! CPU_INFORMATION::X86CpuInfo when #ProcessorArchitecture is set to
+ //! crashpad::kMinidumpCPUArchitectureAMD64. Minidump files produced by
+ //! `dbghelp.dll` on Windows express CPU_INFORMATION::OtherCpuInfo in this
+ //! case.
+ CPU_INFORMATION Cpu;
+};
+
+//! \brief Information about a specific thread within the process.
+//!
+//! \sa MINIDUMP_THREAD_LIST
+struct __attribute__((packed, aligned(4))) MINIDUMP_THREAD {
+ //! \brief The thread’s ID. This may be referenced by
+ //! MINIDUMP_EXCEPTION_STREAM::ThreadId.
+ uint32_t ThreadId;
+
+ //! \brief The thread’s suspend count.
+ //!
+ //! This field will be `0` if the thread is schedulable (not suspended).
+ uint32_t SuspendCount;
+
+ //! \brief The thread’s priority class.
+ //!
+ //! On Windows, this is a `*_PRIORITY_CLASS` value. `NORMAL_PRIORITY_CLASS`
+ //! has value `0x20`; higher priority classes have higher values.
+ uint32_t PriorityClass;
+
+ //! \brief The thread’s priority level.
+ //!
+ //! On Windows, this is a `THREAD_PRIORITY_*` value. `THREAD_PRIORITY_NORMAL`
+ //! has value `0`; higher priorities have higher values, and lower priorities
+ //! have lower (negative) values.
+ uint32_t Priority;
+
+ //! \brief The address of the thread’s thread environment block in the address
+ //! space of the process that the minidump file contains a snapshot of.
+ //!
+ //! The thread environment block contains thread-local data.
+ //!
+ //! A MINIDUMP_MEMORY_DESCRIPTOR may be present in the MINIDUMP_MEMORY_LIST
+ //! stream containing the thread-local data pointed to by this field.
+ uint64_t Teb;
+
+ //! \brief A snapshot of the thread’s stack.
+ //!
+ //! A MINIDUMP_MEMORY_DESCRIPTOR may be present in the MINIDUMP_MEMORY_LIST
+ //! stream containing a pointer to the same memory range referenced by this
+ //! field.
+ MINIDUMP_MEMORY_DESCRIPTOR Stack;
+
+ //! \brief A pointer to a CPU-specific CONTEXT structure containing the
+ //! thread’s context at the time the snapshot was taken.
+ //!
+ //! If the minidump file was generated as a result of an exception taken on
+ //! this thread, this field may identify a different context than the
+ //! exception context. For these minidump files, a MINIDUMP_EXCEPTION_STREAM
+ //! stream will be present, and the context contained within that stream will
+ //! be the exception context.
+ //!
+ //! The interpretation of the context structure is dependent on the CPU
+ //! architecture identified by MINIDUMP_SYSTEM_INFO::ProcessorArchitecture.
+ //! For crashpad::kMinidumpCPUArchitectureX86, this will be
+ //! crashpad::MinidumpContextX86. For crashpad::kMinidumpCPUArchitectureAMD64,
+ //! this will be crashpad::MinidumpContextAMD64.
+ MINIDUMP_LOCATION_DESCRIPTOR ThreadContext;
+};
+
+//! \brief Information about all threads within the process.
+struct __attribute__((packed, aligned(4))) MINIDUMP_THREAD_LIST {
+ //! \brief The number of threads present in the #Threads array.
+ uint32_t NumberOfThreads;
+
+ //! \brief Structures identifying each thread within the process.
+ MINIDUMP_THREAD Threads[0];
+};
+
+//! \brief Information about an exception that occurred in the process.
+struct __attribute__((packed, aligned(4))) MINIDUMP_EXCEPTION {
+ //! \brief The top-level exception code identifying the exception, in
+ //! operating system-specific values.
+ //!
+ //! For Mac OS X minidumps, this will be an \ref EXC_x "EXC_*" exception type,
+ //! such as `EXC_BAD_ACCESS`. `EXC_CRASH` will not appear here for exceptions
+ //! processed as `EXC_CRASH` when generated from another preceding exception:
+ //! the original exception code will appear instead. The exception type as it
+ //! was received will appear at index 0 of #ExceptionInformation.
+ //!
+ //! \note This field is named ExceptionCode, but what is known as the
+ //! “exception code” on Mac OS X/Mach is actually stored in the
+ //! #ExceptionFlags field of a minidump file.
+ //!
+ //! \todo Document the possible values by OS. There may be OS-specific enums
+ //! in minidump_extensions.h.
+ uint32_t ExceptionCode;
+
+ //! \brief Additional exception flags that further identify the exception, in
+ //! operating system-specific values.
+ //!
+ //! For Mac OS X minidumps, this will be the value of the exception code at
+ //! index 0 as received by a Mach exception handler, except:
+ //! * For exception type `EXC_CRASH` generated from another preceding
+ //! exception, the original exception code will appear here, not the code
+ //! as received by the Mach exception handler.
+ //! * For exception types `EXC_RESOURCE` and `EXC_GUARD`, the high 32 bits of
+ //! the code received by the Mach exception handler will appear here.
+ //!
+ //! In all cases for Mac OS X minidumps, the code as it was received by the
+ //! Mach exception handler will appear at index 1 of #ExceptionInformation.
+ //!
+ //! \todo Document the possible values by OS. There may be OS-specific enums
+ //! in minidump_extensions.h.
+ uint32_t ExceptionFlags;
+
+ //! \brief An address, in the address space of the process that this minidump
+ //! file contains a snapshot of, of another MINIDUMP_EXCEPTION. This field
+ //! is used for nested exceptions.
+ uint64_t ExceptionRecord;
+
+ //! \brief The address that caused the exception.
+ //!
+ //! This may be the address that caused a fault on data access, or it may be
+ //! the instruction pointer that contained an offending instruction.
+ uint64_t ExceptionAddress;
+
+ //! \brief The number of valid elements in #ExceptionInformation.
+ uint32_t NumberParameters;
+
+ uint32_t __unusedAlignment;
+
+ //! \brief Additional information about the exception, specific to the
+ //! operating system and possibly the #ExceptionCode.
+ //!
+ //! For Mac OS X minidumps, this will contain the exception type as received
+ //! by a Mach exception handler and the values of the `codes[0]` and
+ //! `codes[1]` (exception code and subcode) parameters supplied to the Mach
+ //! exception handler. Unlike #ExceptionCode and #ExceptionFlags, the values
+ //! received by a Mach exception handler are used directly here even for the
+ //! `EXC_CRASH`, `EXC_RESOURCE`, and `EXC_GUARD` exception types.
+ uint64_t ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
+};
+
+//! \brief Information about the exception that triggered a minidump file’s
+//! generation.
+struct __attribute__((packed, aligned(4))) MINIDUMP_EXCEPTION_STREAM {
+ //! \brief The ID of the thread that caused the exception.
+ //!
+ //! \sa MINIDUMP_THREAD::ThreadId
+ uint32_t ThreadId;
+
+ uint32_t __alignment;
+
+ //! \brief Information about the exception.
+ MINIDUMP_EXCEPTION ExceptionRecord;
+
+ //! \brief A pointer to a CPU-specific CONTEXT structure containing the
+ //! thread’s context at the time the exception was caused.
+ //!
+ //! The interpretation of the context structure is dependent on the CPU
+ //! architecture identified by MINIDUMP_SYSTEM_INFO::ProcessorArchitecture.
+ //! For crashpad::kMinidumpCPUArchitectureX86, this will be
+ //! crashpad::MinidumpContextX86. For crashpad::kMinidumpCPUArchitectureAMD64,
+ //! this will be crashpad::MinidumpContextAMD64.
+ MINIDUMP_LOCATION_DESCRIPTOR ThreadContext;
+};
+
+//! \brief Information about a specific module loaded within the process at the
+//! time the snapshot was taken.
+//!
+//! A module may be the main executable, a shared library, or a loadable module.
+//!
+//! \sa MINIDUMP_MODULE_LIST
+struct __attribute__((packed, aligned(4))) MINIDUMP_MODULE {
+ //! \brief The base address of the loaded module in the address space of the
+ //! process that the minidump file contains a snapshot of.
+ uint64_t BaseOfImage;
+
+ //! \brief The size of the loaded module.
+ uint32_t SizeOfImage;
+
+ //! \brief The loaded module’s checksum, or `0` if unknown.
+ //!
+ //! On Windows, this field comes from the `CheckSum` field of the module’s
+ //! `IMAGE_OPTIONAL_HEADER` structure, if present. It reflects the checksum at
+ //! the time the module was linked.
+ uint32_t CheckSum;
+
+ //! \brief The module’s timestamp, in `time_t` units, seconds since the POSIX
+ //! epoch.
+ //!
+ //! On Windows, this field comes from the `TimeDateStamp` field of the
+ //! module’s `IMAGE_HEADER` structure. It reflects the timestamp at the time
+ //! the module was linked.
+ uint32_t TimeDateStamp;
+
+ //! \brief ::RVA of a MINIDUMP_STRING containing the module’s path or file
+ //! name.
+ RVA ModuleNameRva;
+
+ //! \brief The module’s version information.
+ VS_FIXEDFILEINFO VersionInfo;
+
+ //! \brief A pointer to the module’s CodeView record, typically a link to its
+ //! debugging information in crashpad::MinidumpModuleCodeViewRecordPDB70
+ //! format.
+ //!
+ //! The specific format of the CodeView record is indicated by its signature,
+ //! the first 32-bit value in the structure. For links to debugging
+ //! information in contemporary usage, this is normally a
+ //! crashpad::MinidumpModuleCodeViewRecordPDB70 structure, but may be a
+ //! crashpad::MinidumpModuleCodeViewRecordPDB20 structure instead. These
+ //! structures identify a link to debugging data within a `.pdb` (Program
+ //! Database) file. See <a
+ //! href="http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles">Matching
+ //! Debug Information</a>, PDB Files.
+ //!
+ //! On Windows, it is also possible for the CodeView record to contain
+ //! debugging information itself, as opposed to a link to a `.pdb` file. See
+ //! <a
+ //! href="http://pierrelib.pagesperso-orange.fr/exec_formats/MS_Symbol_Type_v1.0.pdf#page=71">Microsoft
+ //! Symbol and Type Information</a>, section 7.2, “Debug Information Format”
+ //! for a list of debug information formats, and <a
+ //! href="http://undocumented.rawol.com/sbs-w2k-1-windows-2000-debugging-support.pdf#page=63">Undocumented
+ //! Windows 2000 Secrets</a>, Windows 2000 Debugging Support/Microsoft Symbol
+ //! File Internals/CodeView Subsections for an in-depth description of the
+ //! CodeView 4.1 format. Signatures seen in the wild include “NB09”
+ //! (0x3930424e) for CodeView 4.1 and “NB11” (0x3131424e) for CodeView 5.0.
+ //! This form of debugging information within the module, as opposed to a link
+ //! to an external `.pdb` file, is chosen by building with `/Z7`.
+ //!
+ //! On Windows, the CodeView record is taken from a module’s
+ //! IMAGE_DEBUG_DIRECTORY entry whose Type field has the value
+ //! IMAGE_DEBUG_TYPE_CODEVIEW (`2`), if any. Records in
+ //! crashpad::MinidumpModuleCodeViewRecordPDB70 format are generated by Visual
+ //! Studio .NET (2002) (version 7.0) and later.
+ //!
+ //! When the CodeView record is not present, the fields of this
+ //! MINIDUMP_LOCATION_DESCRIPTOR will be `0`.
+ MINIDUMP_LOCATION_DESCRIPTOR CvRecord;
+
+ //! \brief A pointer to the module’s miscellaneous debugging record, a
+ //! structure of type IMAGE_DEBUG_MISC.
+ //!
+ //! This field is Windows-specific, and has no meaning on other operating
+ //! systems. It is largely obsolete on Windows, where it was used to link to
+ //! debugging information stored in a `.dbg` file. `.dbg` files have been
+ //! superseded by `.pdb` files.
+ //!
+ //! On Windows, the miscellaneous debugging record is taken from module’s
+ //! IMAGE_DEBUG_DIRECTORY entry whose Type field has the value
+ //! IMAGE_DEBUG_TYPE_MISC (`4`), if any.
+ //!
+ //! When the miscellaneous debugging record is not present, the fields of this
+ //! MINIDUMP_LOCATION_DESCRIPTOR will be `0`.
+ //!
+ //! \sa #CvRecord
+ MINIDUMP_LOCATION_DESCRIPTOR MiscRecord;
+
+ uint64_t Reserved0;
+ uint64_t Reserved1;
+};
+
+//! \brief Information about all modules loaded within the process at the time
+//! the snapshot was taken.
+struct __attribute__((packed, aligned(4))) MINIDUMP_MODULE_LIST {
+ //! \brief The number of modules present in the #Modules array.
+ uint32_t NumberOfModules;
+
+ //! \brief Structures identifying each module present in the minidump file.
+ MINIDUMP_MODULE Modules[0];
+};
+
+//! \brief Information about memory regions within the process.
+//!
+//! Typically, a minidump file will not contain a snapshot of a process’ entire
+//! memory image. For minidump files identified as ::MiniDumpNormal in
+//! MINIDUMP_HEADER::Flags, memory regions are limited to those referenced by
+//! MINIDUMP_THREAD::Stack fields, and a small number of others possibly related
+//! to the exception that triggered the snapshot to be taken.
+struct __attribute__((packed, aligned(4))) MINIDUMP_MEMORY_LIST {
+ //! \brief The number of memory regions present in the #MemoryRanges array.
+ uint32_t NumberOfMemoryRanges;
+
+ //! \brief Structures identifying each memory region present in the minidump
+ //! file.
+ MINIDUMP_MEMORY_DESCRIPTOR MemoryRanges[0];
+};
+
+//! \anchor MINIDUMP_MISCx
+//! \name MINIDUMP_MISC*
+//!
+//! \brief Field validity flag values for MINIDUMP_MISC_INFO::Flags1.
+//! \{
+
+//! \brief MINIDUMP_MISC_INFO::ProcessId is valid.
+#define MINIDUMP_MISC1_PROCESS_ID 0x00000001
+
+//! \brief The time-related fields in MINIDUMP_MISC_INFO are valid.
+//!
+//! The following fields are valid:
+//! - MINIDUMP_MISC_INFO::ProcessCreateTime
+//! - MINIDUMP_MISC_INFO::ProcessUserTime
+//! - MINIDUMP_MISC_INFO::ProcessKernelTime
+#define MINIDUMP_MISC1_PROCESS_TIMES 0x00000002
+
+//! \brief The CPU-related fields in MINIDUMP_MISC_INFO_2 are valid.
+//!
+//! The following fields are valid:
+//! - MINIDUMP_MISC_INFO_2::ProcessorMaxMhz
+//! - MINIDUMP_MISC_INFO_2::ProcessorCurrentMhz
+//! - MINIDUMP_MISC_INFO_2::ProcessorMhzLimit
+//! - MINIDUMP_MISC_INFO_2::ProcessorMaxIdleState
+//! - MINIDUMP_MISC_INFO_2::ProcessorCurrentIdleState
+//!
+//! \note This macro should likely have been named
+//! MINIDUMP_MISC2_PROCESSOR_POWER_INFO.
+#define MINIDUMP_MISC1_PROCESSOR_POWER_INFO 0x00000004
+
+//! \brief MINIDUMP_MISC_INFO_3::ProcessIntegrityLevel is valid.
+#define MINIDUMP_MISC3_PROCESS_INTEGRITY 0x00000010
+
+//! \brief MINIDUMP_MISC_INFO_3::ProcessExecuteFlags is valid.
+#define MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS 0x00000020
+
+//! \brief The time zone-related fields in MINIDUMP_MISC_INFO_3 are valid.
+//!
+//! The following fields are valid:
+//! - MINIDUMP_MISC_INFO_3::TimeZoneId
+//! - MINIDUMP_MISC_INFO_3::TimeZone
+#define MINIDUMP_MISC3_TIMEZONE 0x00000040
+
+//! \brief MINIDUMP_MISC_INFO_3::ProtectedProcess is valid.
+#define MINIDUMP_MISC3_PROTECTED_PROCESS 0x00000080
+
+//! \brief The build string-related fields in MINIDUMP_MISC_INFO_4 are valid.
+//!
+//! The following fields are valid:
+//! - MINIDUMP_MISC_INFO_4::BuildString
+//! - MINIDUMP_MISC_INFO_4::DbgBldStr
+#define MINIDUMP_MISC4_BUILDSTRING 0x00000100
+//! \}
+
+//! \brief Information about the process that the minidump file contains a
+//! snapshot of, as well as the system that hosted that process.
+//!
+//! \sa \ref MINIDUMP_MISCx "MINIDUMP_MISC*"
+//! \sa MINIDUMP_MISC_INFO_2
+//! \sa MINIDUMP_MISC_INFO_3
+//! \sa MINIDUMP_MISC_INFO_4
+//! \sa MINIDUMP_MISC_INFO_N
+struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO {
+ //! \brief The size of the structure.
+ //!
+ //! This field can be used to distinguish between different versions of this
+ //! structure: MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2, MINIDUMP_MISC_INFO_3,
+ //! and MINIDUMP_MISC_INFO_4.
+ //!
+ //! \sa Flags1
+ uint32_t SizeOfInfo;
+
+ //! \brief A bit field of \ref MINIDUMP_MISCx "MINIDUMP_MISC*" values
+ //! indicating which fields of this structure contain valid data.
+ uint32_t Flags1;
+
+ //! \brief The process ID of the process.
+ uint32_t ProcessId;
+
+ //! \brief The time that the process started, in `time_t` units, seconds since
+ //! the POSIX epoch.
+ uint32_t ProcessCreateTime;
+
+ //! \brief The amount of user-mode CPU time used by the process, in seconds,
+ //! at the time of the snapshot.
+ uint32_t ProcessUserTime;
+
+ //! \brief The amount of system-mode (kernel) CPU time used by the process, in
+ //! seconds, at the time of the snapshot.
+ uint32_t ProcessKernelTime;
+};
+
+//! \brief Information about the process that the minidump file contains a
+//! snapshot of, as well as the system that hosted that process.
+//!
+//! This structure variant is used on Windows Vista (NT 6.0) and later.
+//!
+//! \sa \ref MINIDUMP_MISCx "MINIDUMP_MISC*"
+//! \sa MINIDUMP_MISC_INFO
+//! \sa MINIDUMP_MISC_INFO_3
+//! \sa MINIDUMP_MISC_INFO_4
+//! \sa MINIDUMP_MISC_INFO_N
+struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_2
+ : public MINIDUMP_MISC_INFO {
+ //! \brief The maximum clock rate of the system’s CPU or CPUs, in MHz.
+ uint32_t ProcessorMaxMhz;
+
+ //! \brief The clock rate of the system’s CPU or CPUs, in MHz, at the time of
+ //! the snapshot.
+ uint32_t ProcessorCurrentMhz;
+
+ //! \brief The maximum clock rate of the system’s CPU or CPUs, in MHz, reduced
+ //! by any thermal limitations, at the time of the snapshot.
+ uint32_t ProcessorMhzLimit;
+
+ //! \brief The maximum idle state of the system’s CPU or CPUs.
+ uint32_t ProcessorMaxIdleState;
+
+ //! \brief The idle state of the system’s CPU or CPUs at the time of the
+ //! snapshot.
+ uint32_t ProcessorCurrentIdleState;
+};
+
+//! \brief Information about the process that the minidump file contains a
+//! snapshot of, as well as the system that hosted that process.
+//!
+//! This structure variant is used on Windows 7 (NT 6.1) and later.
+//!
+//! \sa \ref MINIDUMP_MISCx "MINIDUMP_MISC*"
+//! \sa MINIDUMP_MISC_INFO
+//! \sa MINIDUMP_MISC_INFO_2
+//! \sa MINIDUMP_MISC_INFO_4
+//! \sa MINIDUMP_MISC_INFO_N
+struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_3
+ : public MINIDUMP_MISC_INFO_2 {
+ //! \brief The process’ integrity level.
+ //!
+ //! Windows typically uses `SECURITY_MANDATORY_MEDIUM_RID` (0x2000) for
+ //! processes belonging to normal authenticated users and
+ //! `SECURITY_MANDATORY_HIGH_RID` (0x3000) for elevated processes.
+ //!
+ //! This field is Windows-specific, and has no meaning on other operating
+ //! systems.
+ uint32_t ProcessIntegrityLevel;
+
+ //! \brief The process’ execute flags.
+ //!
+ //! On Windows, this appears to be returned by `NtQueryInformationProcess()`
+ //! with an argument of `ProcessExecuteFlags` (34).
+ //!
+ //! This field is Windows-specific, and has no meaning on other operating
+ //! systems.
+ uint32_t ProcessExecuteFlags;
+
+ //! \brief Whether the process is protected.
+ //!
+ //! This field is Windows-specific, and has no meaning on other operating
+ //! systems.
+ uint32_t ProtectedProcess;
+
+ //! \brief Whether daylight saving time was being observed in the system’s
+ //! location at the time of the snapshot.
+ //!
+ //! This field can contain the following values:
+ //! - `0` if the location does not observe daylight saving time at all. The
+ //! TIME_ZONE_INFORMATION::StandardName field of #TimeZoneId contains the
+ //! time zone name.
+ //! - `1` if the location observes daylight saving time, but standard time
+ //! was in effect at the time of the snapshot. The
+ //! TIME_ZONE_INFORMATION::StandardName field of #TimeZoneId contains the
+ //! time zone name.
+ //! - `2` if the location observes daylight saving time, and it was in effect
+ //! at the time of the snapshot. The TIME_ZONE_INFORMATION::DaylightName
+ //! field of #TimeZoneId contains the time zone name.
+ //!
+ //! \sa #TimeZone
+ uint32_t TimeZoneId;
+
+ //! \brief Information about the time zone at the system’s location.
+ //!
+ //! \sa #TimeZoneId
+ TIME_ZONE_INFORMATION TimeZone;
+};
+
+//! \brief Information about the process that the minidump file contains a
+//! snapshot of, as well as the system that hosted that process.
+//!
+//! This structure variant is used on Windows 8 (NT 6.2) and later.
+//!
+//! \sa \ref MINIDUMP_MISCx "MINIDUMP_MISC*"
+//! \sa MINIDUMP_MISC_INFO
+//! \sa MINIDUMP_MISC_INFO_2
+//! \sa MINIDUMP_MISC_INFO_3
+//! \sa MINIDUMP_MISC_INFO_N
+struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_4
+ : public MINIDUMP_MISC_INFO_3 {
+ //! \brief The operating system’s “build string”, a string identifying a
+ //! specific build of the operating system.
+ //!
+ //! This string is UTF-16-encoded and terminated by a UTF-16 `NUL` code unit.
+ //!
+ //! On Windows 8.1 (NT 6.3), this is “6.3.9600.17031
+ //! (winblue_gdr.140221-1952)”.
+ base::char16 BuildString[260];
+
+ //! \brief The minidump producer’s “build string”, a string identifying the
+ //! module that produced a minidump file.
+ //!
+ //! This string is UTF-16-encoded and terminated by a UTF-16 `NUL` code unit.
+ //!
+ //! On Windows 8.1 (NT 6.3), this may be “dbghelp.i386,6.3.9600.16520” or
+ //! “dbghelp.amd64,6.3.9600.16520” depending on CPU architecture.
+ base::char16 DbgBldStr[40];
+};
+
+//! \brief The latest known version of the MINIDUMP_MISC_INFO structure.
+typedef MINIDUMP_MISC_INFO_4 MINIDUMP_MISC_INFO_N;
+
+//! \brief Minidump file type values for MINIDUMP_HEADER::Flags. These bits
+//! describe the types of data carried within a minidump file.
+enum MINIDUMP_TYPE {
+ //! \brief A minidump file without any additional data.
+ //!
+ //! This type of minidump file contains:
+ //! - A MINIDUMP_SYSTEM_INFO stream.
+ //! - A MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2, MINIDUMP_MISC_INFO_3, or
+ //! MINIDUMP_MISC_INFO_4 stream, depending on which fields are present.
+ //! - A MINIDUMP_THREAD_LIST stream. All threads are present, along with a
+ //! snapshot of each thread’s stack memory sufficient to obtain backtraces.
+ //! - If the minidump file was generated as a result of an exception, a
+ //! MINIDUMP_EXCEPTION_STREAM describing the exception.
+ //! - A MINIDUMP_MODULE_LIST stream. All loaded modules are present.
+ //! - Typically, a MINIDUMP_MEMORY_LIST stream containing duplicate pointers
+ //! to the stack memory regions also referenced by the MINIDUMP_THREAD_LIST
+ //! stream. This type of minidump file also includes a
+ //! MINIDUMP_MEMORY_DESCRIPTOR containing the 256 bytes centered around
+ //! the exception address or the instruction pointer.
+ MiniDumpNormal = 0x00000000,
+};
+
+#endif // CRASHPAD_COMPAT_NON_WIN_DBGHELP_H_
diff --git a/chromium/third_party/crashpad/crashpad/compat/non_win/minwinbase.h b/chromium/third_party/crashpad/crashpad/compat/non_win/minwinbase.h
new file mode 100644
index 00000000000..2761b1cccd2
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/non_win/minwinbase.h
@@ -0,0 +1,49 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_COMPAT_NON_WIN_MINWINBASE_H_
+#define CRASHPAD_COMPAT_NON_WIN_MINWINBASE_H_
+
+#include <stdint.h>
+
+//! \brief Represents a date and time.
+struct SYSTEMTIME {
+ //! \brief The year, represented fully.
+ //!
+ //! The year 2014 would be represented in this field as `2014`.
+ uint16_t wYear;
+
+ //! \brief The month of the year, `1` for January and `12` for December.
+ uint16_t wMonth;
+
+ //! \brief The day of the week, `0` for Sunday and `6` for Saturday.
+ uint16_t wDayOfWeek;
+
+ //! \brief The day of the month, `1` through `31`.
+ uint16_t wDay;
+
+ //! \brief The hour of the day, `0` through `23`.
+ uint16_t wHour;
+
+ //! \brief The minute of the hour, `0` through `59`.
+ uint16_t wMinute;
+
+ //! \brief The second of the minute, `0` through `60`.
+ uint16_t wSecond;
+
+ //! \brief The millisecond of the second, `0` through `999`.
+ uint16_t wMilliseconds;
+};
+
+#endif // CRASHPAD_COMPAT_NON_WIN_MINWINBASE_H_
diff --git a/chromium/third_party/crashpad/crashpad/compat/non_win/timezoneapi.h b/chromium/third_party/crashpad/crashpad/compat/non_win/timezoneapi.h
new file mode 100644
index 00000000000..59efae286d9
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/non_win/timezoneapi.h
@@ -0,0 +1,61 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_COMPAT_NON_WIN_TIMEZONEAPI_H_
+#define CRASHPAD_COMPAT_NON_WIN_TIMEZONEAPI_H_
+
+#include <stdint.h>
+
+#include "base/strings/string16.h"
+#include "compat/non_win/minwinbase.h"
+
+//! \brief Information about a time zone and its daylight saving rules.
+struct TIME_ZONE_INFORMATION {
+ //! \brief The number of minutes west of UTC.
+ int32_t Bias;
+
+ //! \brief The UTF-16-encoded name of the time zone when observing standard
+ //! time.
+ base::char16 StandardName[32];
+
+ //! \brief The date and time to switch from daylight saving time to standard
+ //! time.
+ //!
+ //! This can be a specific time, or with SYSTEMTIME::wYear set to `0`, it can
+ //! reflect an annual recurring transition. In that case, SYSTEMTIME::wDay in
+ //! the range `1` to `5` is interpreted as the given occurrence of
+ //! SYSTEMTIME::wDayOfWeek within the month, `1` being the first occurrence
+ //! and `5` being the last (even if there are fewer than 5).
+ SYSTEMTIME StandardDate;
+
+ //! \brief The bias relative to #Bias to be applied when observing standard
+ //! time.
+ int32_t StandardBias;
+
+ //! \brief The UTF-16-encoded name of the time zone when observing daylight
+ //! saving time.
+ base::char16 DaylightName[32];
+
+ //! \brief The date and time to switch from standard time to daylight saving
+ //! time.
+ //!
+ //! This field is specified in the same manner as #StandardDate.
+ SYSTEMTIME DaylightDate;
+
+ //! \brief The bias relative to #Bias to be applied when observing daylight
+ //! saving time.
+ int32_t DaylightBias;
+};
+
+#endif // CRASHPAD_COMPAT_NON_WIN_TIMEZONEAPI_H_
diff --git a/chromium/third_party/crashpad/crashpad/compat/non_win/verrsrc.h b/chromium/third_party/crashpad/crashpad/compat/non_win/verrsrc.h
new file mode 100644
index 00000000000..70f53442404
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/non_win/verrsrc.h
@@ -0,0 +1,184 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_COMPAT_NON_WIN_VERRSRC_H_
+#define CRASHPAD_COMPAT_NON_WIN_VERRSRC_H_
+
+#include <stdint.h>
+
+//! \file
+
+//! \brief The magic number for a VS_FIXEDFILEINFO structure, stored in
+//! VS_FIXEDFILEINFO::dwSignature.
+#define VS_FFI_SIGNATURE 0xfeef04bd
+
+//! \brief The version of a VS_FIXEDFILEINFO structure, stored in
+//! VS_FIXEDFILEINFO::dwStrucVersion.
+#define VS_FFI_STRUCVERSION 0x00010000
+
+//! \anchor VS_FF_x
+//! \name VS_FF_*
+//!
+//! \brief File attribute values for VS_FIXEDFILEINFO::dwFileFlags and
+//! VS_FIXEDFILEINFO::dwFileFlagsMask.
+//! \{
+#define VS_FF_DEBUG 0x00000001
+#define VS_FF_PRERELEASE 0x00000002
+#define VS_FF_PATCHED 0x00000004
+#define VS_FF_PRIVATEBUILD 0x00000008
+#define VS_FF_INFOINFERRED 0x00000010
+#define VS_FF_SPECIALBUILD 0x00000020
+//! \}
+
+//! \anchor VOS_x
+//! \name VOS_*
+//!
+//! \brief Operating system values for VS_FIXEDFILEINFO::dwFileOS.
+//! \{
+#define VOS_UNKNOWN 0x00000000
+#define VOS_DOS 0x00010000
+#define VOS_OS216 0x00020000
+#define VOS_OS232 0x00030000
+#define VOS_NT 0x00040000
+#define VOS_WINCE 0x00050000
+#define VOS__BASE 0x00000000
+#define VOS__WINDOWS16 0x00000001
+#define VOS__PM16 0x00000002
+#define VOS__PM32 0x00000003
+#define VOS__WINDOWS32 0x00000004
+#define VOS_DOS_WINDOWS16 0x00010001
+#define VOS_DOS_WINDOWS32 0x00010004
+#define VOS_OS216_PM16 0x00020002
+#define VOS_OS232_PM32 0x00030003
+#define VOS_NT_WINDOWS32 0x00040004
+//! \}
+
+//! \anchor VFT_x
+//! \name VFT_*
+//!
+//! \brief File type values for VS_FIXEDFILEINFO::dwFileType.
+//! \{
+#define VFT_UNKNOWN 0x00000000
+#define VFT_APP 0x00000001
+#define VFT_DLL 0x00000002
+#define VFT_DRV 0x00000003
+#define VFT_FONT 0x00000004
+#define VFT_VXD 0x00000005
+#define VFT_STATIC_LIB 0x00000007
+//! \}
+
+//! \anchor VFT2_x
+//! \name VFT2_*
+//!
+//! \brief File subtype values for VS_FIXEDFILEINFO::dwFileSubtype.
+//! \{
+#define VFT2_UNKNOWN 0x00000000
+#define VFT2_DRV_PRINTER 0x00000001
+#define VFT2_DRV_KEYBOARD 0x00000002
+#define VFT2_DRV_LANGUAGE 0x00000003
+#define VFT2_DRV_DISPLAY 0x00000004
+#define VFT2_DRV_MOUSE 0x00000005
+#define VFT2_DRV_NETWORK 0x00000006
+#define VFT2_DRV_SYSTEM 0x00000007
+#define VFT2_DRV_INSTALLABLE 0x00000008
+#define VFT2_DRV_SOUND 0x00000009
+#define VFT2_DRV_COMM 0x0000000A
+#define VFT2_DRV_INPUTMETHOD 0x0000000B
+#define VFT2_DRV_VERSIONED_PRINTER 0x0000000C
+#define VFT2_FONT_RASTER 0x00000001
+#define VFT2_FONT_VECTOR 0x00000002
+#define VFT2_FONT_TRUETYPE 0x00000003
+//! \}
+
+//! \brief Version information for a file.
+//!
+//! On Windows, this information is derived from a file’s version information
+//! resource, and is obtained by calling `VerQueryValue()` with an `lpSubBlock`
+//! argument of `"\"` (a single backslash).
+struct VS_FIXEDFILEINFO {
+ //! \brief The structure’s magic number, ::VS_FFI_SIGNATURE.
+ uint32_t dwSignature;
+
+ //! \brief The structure’s version, ::VS_FFI_STRUCVERSION.
+ uint32_t dwStrucVersion;
+
+ //! \brief The more-significant portion of the file’s version number.
+ //!
+ //! This field contains the first two components of a four-component version
+ //! number. For a file whose version is 1.2.3.4, this field would be
+ //! `0x00010002`.
+ //!
+ //! \sa dwFileVersionLS
+ uint32_t dwFileVersionMS;
+
+ //! \brief The less-significant portion of the file’s version number.
+ //!
+ //! This field contains the last two components of a four-component version
+ //! number. For a file whose version is 1.2.3.4, this field would be
+ //! `0x00030004`.
+ //!
+ //! \sa dwFileVersionMS
+ uint32_t dwFileVersionLS;
+
+ //! \brief The more-significant portion of the product’s version number.
+ //!
+ //! This field contains the first two components of a four-component version
+ //! number. For a product whose version is 1.2.3.4, this field would be
+ //! `0x00010002`.
+ //!
+ //! \sa dwProductVersionLS
+ uint32_t dwProductVersionMS;
+
+ //! \brief The less-significant portion of the product’s version number.
+ //!
+ //! This field contains the last two components of a four-component version
+ //! number. For a product whose version is 1.2.3.4, this field would be
+ //! `0x00030004`.
+ //!
+ //! \sa dwProductVersionMS
+ uint32_t dwProductVersionLS;
+
+ //! \brief A bitmask of \ref VS_FF_x "VS_FF_*" values indicating which bits in
+ //! #dwFileFlags are valid.
+ uint32_t dwFileFlagsMask;
+
+ //! \brief A bitmask of \ref VS_FF_x "VS_FF_*" values identifying attributes
+ //! of the file. Only bits present in #dwFileFlagsMask are valid.
+ uint32_t dwFileFlags;
+
+ //! \brief The file’s intended operating system, a value of \ref VOS_x
+ //! "VOS_*".
+ uint32_t dwFileOS;
+
+ //! \brief The file’s type, a value of \ref VFT_x "VFT_*".
+ uint32_t dwFileType;
+
+ //! \brief The file’s subtype, a value of \ref VFT2_x "VFT2_*" corresponding
+ //! to its #dwFileType, if the file type has subtypes.
+ uint32_t dwFileSubtype;
+
+ //! \brief The more-significant portion of the file’s creation date.
+ //!
+ //! The intended encoding of this field is unknown. This field is unused and
+ //! always has the value `0`.
+ uint32_t dwFileDateMS;
+
+ //! \brief The less-significant portion of the file’s creation date.
+ //!
+ //! The intended encoding of this field is unknown. This field is unused and
+ //! always has the value `0`.
+ uint32_t dwFileDateLS;
+};
+
+#endif // CRASHPAD_COMPAT_NON_WIN_VERRSRC_H_
diff --git a/chromium/third_party/crashpad/crashpad/compat/non_win/windows.h b/chromium/third_party/crashpad/crashpad/compat/non_win/windows.h
new file mode 100644
index 00000000000..45774514936
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/non_win/windows.h
@@ -0,0 +1,17 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// dbghelp.h on Windows requires inclusion of windows.h before it. To avoid
+// cluttering all inclusions of dbghelp.h with #ifdefs, always include windows.h
+// and have an empty one on non-Windows platforms.
diff --git a/chromium/third_party/crashpad/crashpad/compat/non_win/winnt.h b/chromium/third_party/crashpad/crashpad/compat/non_win/winnt.h
new file mode 100644
index 00000000000..d61504c09c7
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/non_win/winnt.h
@@ -0,0 +1,182 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_COMPAT_NON_WIN_WINNT_H_
+#define CRASHPAD_COMPAT_NON_WIN_WINNT_H_
+
+//! \file
+
+//! \anchor VER_SUITE_x
+//! \name VER_SUITE_*
+//!
+//! \brief Installable product values for MINIDUMP_SYSTEM_INFO::SuiteMask.
+//! \{
+#define VER_SUITE_SMALLBUSINESS 0x0001
+#define VER_SUITE_ENTERPRISE 0x0002
+#define VER_SUITE_BACKOFFICE 0x0004
+#define VER_SUITE_COMMUNICATIONS 0x0008
+#define VER_SUITE_TERMINAL 0x0010
+#define VER_SUITE_SMALLBUSINESS_RESTRICTED 0x0020
+#define VER_SUITE_EMBEDDEDNT 0x0040
+#define VER_SUITE_DATACENTER 0x0080
+#define VER_SUITE_SINGLEUSERTS 0x0100
+#define VER_SUITE_PERSONAL 0x0200
+#define VER_SUITE_BLADE 0x0400
+#define VER_SUITE_EMBEDDED_RESTRICTED 0x0800
+#define VER_SUITE_SECURITY_APPLIANCE 0x1000
+#define VER_SUITE_STORAGE_SERVER 0x2000
+#define VER_SUITE_COMPUTE_SERVER 0x4000
+#define VER_SUITE_WH_SERVER 0x8000
+//! \}
+
+//! \brief The maximum number of exception parameters present in the
+//! MINIDUMP_EXCEPTION::ExceptionInformation array.
+#define EXCEPTION_MAXIMUM_PARAMETERS 15
+
+//! \anchor PROCESSOR_ARCHITECTURE_x
+//! \name PROCESSOR_ARCHITECTURE_*
+//!
+//! \brief CPU type values for MINIDUMP_SYSTEM_INFO::ProcessorArchitecture.
+//!
+//! \sa crashpad::MinidumpCPUArchitecture
+//! \{
+#define PROCESSOR_ARCHITECTURE_INTEL 0
+#define PROCESSOR_ARCHITECTURE_MIPS 1
+#define PROCESSOR_ARCHITECTURE_ALPHA 2
+#define PROCESSOR_ARCHITECTURE_PPC 3
+#define PROCESSOR_ARCHITECTURE_SHX 4
+#define PROCESSOR_ARCHITECTURE_ARM 5
+#define PROCESSOR_ARCHITECTURE_IA64 6
+#define PROCESSOR_ARCHITECTURE_ALPHA64 7
+#define PROCESSOR_ARCHITECTURE_MSIL 8
+#define PROCESSOR_ARCHITECTURE_AMD64 9
+#define PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 10
+#define PROCESSOR_ARCHITECTURE_NEUTRAL 11
+#define PROCESSOR_ARCHITECTURE_UNKNOWN 0xffff
+//! \}
+
+//! \anchor PF_x
+//! \name PF_*
+//!
+//! \brief CPU feature values for \ref CPU_INFORMATION::ProcessorFeatures
+//! "CPU_INFORMATION::OtherCpuInfo::ProcessorFeatures".
+//!
+//! \{
+#define PF_FLOATING_POINT_PRECISION_ERRATA 0
+#define PF_FLOATING_POINT_EMULATED 1
+#define PF_COMPARE_EXCHANGE_DOUBLE 2
+#define PF_MMX_INSTRUCTIONS_AVAILABLE 3
+#define PF_PPC_MOVEMEM_64BIT_OK 4
+#define PF_ALPHA_BYTE_INSTRUCTIONS 5
+#define PF_XMMI_INSTRUCTIONS_AVAILABLE 6
+#define PF_3DNOW_INSTRUCTIONS_AVAILABLE 7
+#define PF_RDTSC_INSTRUCTION_AVAILABLE 8
+#define PF_PAE_ENABLED 9
+#define PF_XMMI64_INSTRUCTIONS_AVAILABLE 10
+#define PF_SSE_DAZ_MODE_AVAILABLE 11
+#define PF_NX_ENABLED 12
+#define PF_SSE3_INSTRUCTIONS_AVAILABLE 13
+#define PF_COMPARE_EXCHANGE128 14
+#define PF_COMPARE64_EXCHANGE128 15
+#define PF_CHANNELS_ENABLED 16
+#define PF_XSAVE_ENABLED 17
+#define PF_ARM_VFP_32_REGISTERS_AVAILABLE 18
+#define PF_ARM_NEON_INSTRUCTIONS_AVAILABLE 19
+#define PF_SECOND_LEVEL_ADDRESS_TRANSLATION 20
+#define PF_VIRT_FIRMWARE_ENABLED 21
+#define PF_RDWRFSGSBASE_AVAILABLE 22
+#define PF_FASTFAIL_AVAILABLE 23
+#define PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE 24
+#define PF_ARM_64BIT_LOADSTORE_ATOMIC 25
+#define PF_ARM_EXTERNAL_CACHE_AVAILABLE 26
+#define PF_ARM_FMAC_INSTRUCTIONS_AVAILABLE 27
+#define PF_RDRAND_INSTRUCTION_AVAILABLE 28
+//! \}
+
+//! \anchor IMAGE_DEBUG_MISC_x
+//! \name IMAGE_DEBUG_MISC_*
+//!
+//! Data type values for IMAGE_DEBUG_MISC::DataType.
+//! \{
+
+//! \brief A pointer to a `.dbg` file.
+//!
+//! IMAGE_DEBUG_MISC::Data will contain the path or file name of the `.dbg` file
+//! associated with the module.
+#define IMAGE_DEBUG_MISC_EXENAME 1
+
+//! \}
+
+//! \brief Miscellaneous debugging record.
+//!
+//! This structure is referenced by MINIDUMP_MODULE::MiscRecord. It is obsolete,
+//! superseded by the CodeView record.
+struct IMAGE_DEBUG_MISC {
+ //! \brief The type of data carried in the #Data field.
+ //!
+ //! This is a value of \ref IMAGE_DEBUG_MISC_x "IMAGE_DEBUG_MISC_*".
+ uint32_t DataType;
+
+ //! \brief The length of this structure in bytes, including the entire #Data
+ //! field and its `NUL` terminator.
+ //!
+ //! \note The Windows documentation states that this field is rounded up to
+ //! nearest nearest 4-byte multiple.
+ uint32_t Length;
+
+ //! \brief The encoding of the #Data field.
+ //!
+ //! If this field is `0`, #Data contains narrow or multibyte character data.
+ //! If this field is `1`, #Data is UTF-16-encoded.
+ //!
+ //! On Windows, with this field set to `0`, #Data will be encoded in the code
+ //! page of the system that linked the module. On other operating systems,
+ //! UTF-8 may be used.
+ uint8_t Unicode;
+
+ uint8_t Reserved[3];
+
+ //! \brief The data carried within this structure.
+ //!
+ //! For string data, this field will be `NUL`-terminated. If #Unicode is `1`,
+ //! this field is UTF-16-encoded, and will be terminated by a UTF-16 `NUL`
+ //! code unit (two `NUL` bytes).
+ uint8_t Data[1];
+};
+
+//! \anchor VER_NT_x
+//! \name VER_NT_*
+//!
+//! \brief Operating system type values for MINIDUMP_SYSTEM_INFO::ProductType.
+//!
+//! \sa crashpad::MinidumpOSType
+//! \{
+#define VER_NT_WORKSTATION 1
+#define VER_NT_DOMAIN_CONTROLLER 2
+#define VER_NT_SERVER 3
+//! \}
+
+//! \anchor VER_PLATFORM_x
+//! \name VER_PLATFORM_*
+//!
+//! \brief Operating system family values for MINIDUMP_SYSTEM_INFO::PlatformId.
+//!
+//! \sa crashpad::MinidumpOS
+//! \{
+#define VER_PLATFORM_WIN32s 0
+#define VER_PLATFORM_WIN32_WINDOWS 1
+#define VER_PLATFORM_WIN32_NT 2
+//! \}
+
+#endif // CRASHPAD_COMPAT_NON_WIN_WINNT_H_
diff --git a/chromium/third_party/crashpad/crashpad/compat/win/getopt.h b/chromium/third_party/crashpad/crashpad/compat/win/getopt.h
new file mode 100644
index 00000000000..45fcbccc391
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/win/getopt.h
@@ -0,0 +1,20 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_COMPAT_WIN_GETOPT_H_
+#define CRASHPAD_COMPAT_WIN_GETOPT_H_
+
+#include "third_party/getopt/getopt.h"
+
+#endif // CRASHPAD_COMPAT_WIN_GETOPT_H_
diff --git a/chromium/third_party/crashpad/crashpad/compat/win/sys/time.h b/chromium/third_party/crashpad/crashpad/compat/win/sys/time.h
new file mode 100644
index 00000000000..46bdef2a381
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/win/sys/time.h
@@ -0,0 +1,23 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_COMPAT_WIN_SYS_TIME_H_
+#define CRASHPAD_COMPAT_WIN_SYS_TIME_H_
+
+struct timeval {
+ long tv_sec;
+ long tv_usec;
+};
+
+#endif // CRASHPAD_COMPAT_WIN_SYS_TIME_H_
diff --git a/chromium/third_party/crashpad/crashpad/compat/win/sys/types.h b/chromium/third_party/crashpad/crashpad/compat/win/sys/types.h
new file mode 100644
index 00000000000..b6463a0c9b1
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/win/sys/types.h
@@ -0,0 +1,31 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_COMPAT_WIN_SYS_TYPES_H_
+#define CRASHPAD_COMPAT_WIN_SYS_TYPES_H_
+
+// This is intended to be roughly equivalent to #include_next.
+#include <../include/sys/types.h>
+
+#include <stdint.h>
+
+#ifdef _WIN64
+typedef int64_t ssize_t;
+#else
+typedef __w64 int ssize_t;
+#endif
+
+typedef unsigned int pid_t;
+
+#endif // CRASHPAD_COMPAT_WIN_SYS_TYPES_H_
diff --git a/chromium/third_party/crashpad/crashpad/compat/win/time.cc b/chromium/third_party/crashpad/crashpad/compat/win/time.cc
new file mode 100644
index 00000000000..e6738879a25
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/win/time.cc
@@ -0,0 +1,40 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <time.h>
+
+extern "C" {
+
+struct tm* gmtime_r(const time_t* timep, struct tm* result) {
+ if (gmtime_s(result, timep) != 0)
+ return nullptr;
+ return result;
+}
+
+struct tm* localtime_r(const time_t* timep, struct tm* result) {
+ if (localtime_s(result, timep) != 0)
+ return nullptr;
+ return result;
+}
+
+const char* strptime(const char* buf, const char* format, struct tm* tm) {
+ // TODO(scottmg): strptime implementation.
+ return nullptr;
+}
+
+time_t timegm(struct tm* tm) {
+ return _mkgmtime(tm);
+}
+
+} // extern "C"
diff --git a/chromium/third_party/crashpad/crashpad/compat/win/time.h b/chromium/third_party/crashpad/crashpad/compat/win/time.h
new file mode 100644
index 00000000000..7c6adc2ce8d
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/win/time.h
@@ -0,0 +1,36 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_COMPAT_WIN_TIME_H_
+#define CRASHPAD_COMPAT_WIN_TIME_H_
+
+#include <../include/time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct tm* gmtime_r(const time_t* timep, struct tm* result);
+
+struct tm* localtime_r(const time_t* timep, struct tm* result);
+
+const char* strptime(const char* buf, const char* format, struct tm* tm);
+
+time_t timegm(struct tm* tm);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // CRASHPAD_COMPAT_WIN_TIME_H_
diff --git a/chromium/third_party/crashpad/crashpad/compat/win/winnt.h b/chromium/third_party/crashpad/crashpad/compat/win/winnt.h
new file mode 100644
index 00000000000..2c5ac662108
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/win/winnt.h
@@ -0,0 +1,32 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_COMPAT_WIN_WINNT_H_
+#define CRASHPAD_COMPAT_WIN_WINNT_H_
+
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa373184.aspx:
+// "Note that this structure definition was accidentally omitted from WinNT.h."
+struct PROCESSOR_POWER_INFORMATION {
+ ULONG Number;
+ ULONG MaxMhz;
+ ULONG CurrentMhz;
+ ULONG MhzLimit;
+ ULONG MaxIdleState;
+ ULONG CurrentIdleState;
+};
+
+// include_next <winnt.h>
+#include <../um/winnt.h>
+
+#endif // CRASHPAD_COMPAT_WIN_WINNT_H_
diff --git a/chromium/third_party/crashpad/crashpad/crashpad.gyp b/chromium/third_party/crashpad/crashpad/crashpad.gyp
new file mode 100644
index 00000000000..fd2a26911cd
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/crashpad.gyp
@@ -0,0 +1,42 @@
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'All',
+ 'type': 'none',
+ 'suppress_wildcard': 1,
+ 'dependencies': [
+ 'client/client.gyp:*',
+ 'client/client_test.gyp:*',
+ 'compat/compat.gyp:*',
+ 'handler/handler.gyp:*',
+ 'minidump/minidump.gyp:*',
+ 'minidump/minidump_test.gyp:*',
+ 'snapshot/snapshot.gyp:*',
+ 'snapshot/snapshot_test.gyp:*',
+ 'test/test.gyp:*',
+ 'test/test_test.gyp:*',
+ 'tools/tools.gyp:*',
+ 'util/util.gyp:*',
+ 'util/util_test.gyp:*',
+ ],
+ 'sources': [
+ 'doc/support/crashpad.doxy.h',
+ 'package.h',
+ ],
+ },
+ ],
+}
diff --git a/chromium/third_party/crashpad/crashpad/doc/developing.ad b/chromium/third_party/crashpad/crashpad/doc/developing.ad
new file mode 100644
index 00000000000..67c3ce4dcc6
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/doc/developing.ad
@@ -0,0 +1,213 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+:doctype: article
+
+= Developing Crashpad
+
+== Status
+
+link:status.html[Project status] information has moved to its own page.
+
+== Introduction
+
+Crashpad is a https://dev.chromium.org/Home[Chromium project]. Most of
+its development practices follow Chromium’s. In order to function on its
+own in other projects, Crashpad uses
+https://chromium.googlesource.com/chromium/mini_chromium/[mini_chromium],
+a small, self-contained library that provides many of Chromium’s useful
+low-level base routines.
+https://chromium.googlesource.com/chromium/mini_chromium/+/master/README[mini_chromium’s
+README] provides more detail.
+
+== Prerequisites
+
+To develop Crashpad, the following tools are necessary, and must be
+present in the `$PATH` environment variable:
+
+ * Chromium’s
+ https://dev.chromium.org/developers/how-tos/depottools[depot_tools].
+ * http://git-scm.com/[Git]. This is provided by Xcode on Mac OS X and by
+ depot_tools on Windows.
+ * https://www.python.org/[Python]. This is provided by the operating system on
+ Mac OS X, and by depot_tools on Windows.
+ * Appropriate development tools. For Mac OS X, this is
+ https://developer.apple.com/xcode/[Xcode], and for Windows, it’s
+ https://www.visualstudio.com/[Visual Studio].
+
+== Getting the Source Code
+
+The main source code repository is a Git repository hosted at
+https://chromium.googlesource.com/crashpad/crashpad. Although it is possible to
+check out this repository directly with `git clone`, Crashpad’s dependencies are
+managed by
+https://dev.chromium.org/developers/how-tos/depottools#TOC-gclient[`gclient`]
+instead of Git submodules, so to work on Crashpad, it is best to use `gclient`
+to get the source code.
+
+`gclient` is part of the
+https://dev.chromium.org/developers/how-tos/depottools[depot_tools]. There’s no
+need to install it separately.
+
+=== Initial Checkout
+
+[subs="verbatim,quotes"]
+----
+$ *mkdir \~/crashpad*
+$ *cd ~/crashpad*
+$ *gclient config --unmanaged https://chromium.googlesource.com/crashpad/crashpad*
+$ *gclient sync*
+----
+
+=== Subsequent Checkouts
+
+[subs="verbatim,quotes"]
+----
+$ *cd ~/crashpad/crashpad*
+$ *git pull -r*
+$ *gclient sync*
+----
+
+== Building
+
+Crashpad uses https://gyp.googlecode.com/[GYP] to generate
+https://martine.github.io/ninja/[Ninja] build files. The build is described by
+`.gyp` files throughout the Crashpad source code tree. The
+`build/gyp_crashpad.py` script runs GYP properly for Crashpad, and is also
+called when you run `gclient sync` or `gclient runhooks`.
+
+The Ninja build files and build output are in the `out` directory. Both debug-
+and release-mode configurations are available. The examples below show the debug
+configuration. To build and test the release configuration, substitute `Release`
+for `Debug`.
+
+[subs="verbatim,quotes"]
+----
+$ *cd ~/crashpad/crashpad*
+$ *ninja -C out/Debug*
+----
+
+Ninja is part of the
+https://dev.chromium.org/developers/how-tos/depottools[depot_tools]. There’s no
+need to install it separately.
+
+== Testing
+
+Crashpad uses https://googletest.googlecode.com/[Google Test] as its
+unit-testing framework, and some tests use
+https://googlemock.googlecode.com/[Google Mock] as well. Its tests are currently
+split up into several test executables, each dedicated to testing a different
+component. This may change in the future. After a successful build, the test
+executables will be found at `out/Debug/crashpad_*_test`.
+
+[subs="verbatim,quotes"]
+----
+$ *cd ~/crashpad/crashpad*
+$ *out/Debug/crashpad_minidump_test*
+$ *out/Debug/crashpad_util_test*
+----
+
+A script is provided to run all of Crashpad’s tests. It accepts a single
+argument that tells it which configuration to test.
+
+[subs="verbatim,quotes"]
+----
+$ *cd ~/crashpad/crashpad*
+$ *python build/run_tests.py Debug*
+----
+
+== Contributing
+
+Crashpad’s contribution process is very similar to
+https://dev.chromium.org/developers/contributing-code[Chromium’s contribution
+process].
+
+=== Code Review
+
+A code review must be conducted for every change to Crashpad’s source code. Code
+review is conducted on https://codereview.chromium.org/[Chromium’s Rietveld]
+system, and all code reviews must be sent to an appropriate reviewer, with a Cc
+sent to
+https://groups.google.com/a/chromium.org/group/crashpad-dev[crashpad-dev]. The
+`codereview.settings` file specifies this environment to `git-cl`.
+
+`git-cl` is part of the
+https://dev.chromium.org/developers/how-tos/depottools[depot_tools]. There’s no
+need to install it separately.
+
+[subs="verbatim,quotes"]
+----
+$ *cd ~/crashpad/crashpad*
+$ *git checkout -b work_branch origin/master*
+_…do some work…_
+$ *git add …*
+$ *git commit*
+$ *git cl upload*
+----
+
+Uploading a patch to Rietveld does not automatically request a review. You must
+select a reviewer and mail your request to them (with a Cc to crashpad-dev) from
+the Rietveld issue page after running `git cl upload`. If you have lost track of
+the issue page, `git cl issue` will remind you of its URL. Alternatively, you
+can request review when uploading to Rietveld by using `git cl upload
+--send-mail`
+
+Git branches maintain their association with Rietveld issues, so if you need to
+make changes based on review feedback, you can do so on the correct Git branch,
+committing your changes locally with `git commit`. You can then upload a new
+patch set with `git cl upload` and let your reviewer know you’ve addressed the
+feedback.
+
+=== Landing Changes
+
+After code review is complete and “LGTM” (“looks good to me”) has been received
+from all reviewers, project members can commit the patch themselves:
+
+[subs="verbatim,quotes"]
+----
+$ *cd ~/crashpad/crashpad*
+$ *git checkout work_branch*
+$ *git cl land*
+----
+
+Crashpad does not currently have a
+https://dev.chromium.org/developers/testing/commit-queue[commit queue], so
+contributors that are not project members will have to ask a project member to
+commit the patch for them. Project members can commit changes on behalf of
+external contributors by patching the change into a local branch and landing it:
+
+[subs="verbatim,quotes"]
+----
+$ *cd ~/crashpad/crashpad*
+$ *git checkout -b for_external_contributor origin/master*
+$ *git cl patch 12345678* _# 12345678 is the Rietveld issue number_
+$ *git cl land -c \'External Contributor <external@contributor.org>'*
+----
+
+=== External Contributions
+
+Copyright holders must complete the
+https://developers.google.com/open-source/cla/individual[Individual Contributor
+License Agreement] or
+https://developers.google.com/open-source/cla/corporate[Corporate Contributor
+License Agreement] as appropriate before any submission can be accepted, and
+must be listed in the `AUTHORS` file. Contributors may be listed in the
+`CONTRIBUTORS` file.
+
+== Buildbot
+
+The https://build.chromium.org/p/client.crashpad/[Crashpad Buildbot] performs
+automated builds and tests of Crashpad. Before checking out or updating the
+Crashpad source code, and after checking in a new change, it is prudent to check
+the Buildbot to ensure that “the tree is green.”
diff --git a/chromium/third_party/crashpad/crashpad/doc/index.ad b/chromium/third_party/crashpad/crashpad/doc/index.ad
new file mode 100644
index 00000000000..852f3897846
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/doc/index.ad
@@ -0,0 +1,42 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+:doctype: article
+
+= Crashpad
+
+https://crashpad.googlecode.com/[Crashpad] is a crash-reporting system.
+
+== Documentation
+
+ * link:status.html[Project status]
+ * link:developing.html[Developing Crashpad]: instructions for getting the
+ source code, building, testing, and contributing to the project.
+ * http://docs.crashpad.googlecode.com/git/doxygen/index.html[Crashpad
+ interface documentation]
+ * http://docs.crashpad.googlecode.com/git/man/[Crashpad tool man pages]
+
+== Source Code
+
+Crashpad’s source code is hosted in a Git repository at
+https://chromium.googlesource.com/crashpad/crashpad.
+
+== Other Links
+
+ * Bugs can be reported at the
+ https://code.google.com/p/crashpad/issues/entry[Crashpad issue tracker].
+ * The https://build.chromium.org/p/client.crashpad[Crashpad Buildbot] performs
+ automated builds and tests.
+ * https://groups.google.com/a/chromium.org/group/crashpad-dev[crashpad-dev] is
+ the Crashpad developers’ mailing list.
diff --git a/chromium/third_party/crashpad/crashpad/doc/status.ad b/chromium/third_party/crashpad/crashpad/doc/status.ad
new file mode 100644
index 00000000000..1d5933b545d
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/doc/status.ad
@@ -0,0 +1,42 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+:doctype: article
+
+= Project Status
+
+== Completed
+
+Crashpad currently consists of a crash-reporting client and some related tools
+for Mac OS X. The core client work for Mac OS X is substantially complete.
+Crashpad has become the crash reporter client for
+https://dev.chromium.org/Home[Chromium] on Mac OS X as of
+https://chromium.googlesource.com/chromium/src/+/d413b2dcb54d523811d386f1ff4084f677a6d089[March
+2015].
+
+== In Progress
+
+Crashpad is actively being bootstrapped on
+https://code.google.com/p/crashpad/issues/detail?id=1[Windows]. With the
+exception of the snapshot library, most major components are now working on
+Windows, and work is progressing on the Windows snapshot implementation.
+
+== Future
+
+There are plans to bring Crashpad clients to other operating systems in the
+future, including
+https://code.google.com/p/crashpad/issues/detail?id=30[Android] and, more
+generically, Linux. There are also plans to implement a
+https://code.google.com/p/crashpad/issues/detail?id=29[crash report processor]
+as part of Crashpad. No timeline for completing this work has been set yet.
diff --git a/chromium/third_party/crashpad/crashpad/doc/support/asciidoc.conf b/chromium/third_party/crashpad/crashpad/doc/support/asciidoc.conf
new file mode 100644
index 00000000000..a4ab2c4c985
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/doc/support/asciidoc.conf
@@ -0,0 +1,58 @@
+# Copyright 2015 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+[miscellaneous]
+# AsciiDoc uses \r\n by default.
+newline=\n
+
+# The default AsciiDoc lang-en.conf uses docdate and doctime for the
+# last-updated line in footer-text. These attributes are taken from the file’s
+# mtime and cannot be overridden. For a git-based project, the date of the last
+# revision is desirable, so change this to use git_date, an attribute that can
+# be computed and passed in by the script that runs AsciiDoc. For man pages, use
+# the mansource and manversion attributes instead of the hard-coded “Version”
+# string and revnumber attribute, so that the version will appear as “Crashpad
+# 0.7.0” as it does in “man” output.
+ifdef::basebackend-html[]
+[footer-text]
+ifdef::doctype-manpage[]
+{mansource=Version} {manversion={revnumber}}{basebackend-xhtml11?<br />}{basebackend-xhtml11=<br>}
+endif::doctype-manpage[]
+ifndef::doctype-manpage[]
+Version {revnumber}{basebackend-xhtml11?<br />}{basebackend-xhtml11=<br>}
+endif::doctype-manpage[]
+Last updated {git_date}
+endif::basebackend-html[]
+
+# The man_link macro was inspired by git’s linkgit macro. See
+# https://github.com/git/git/blob/master/Documentation/asciidoc.conf.
+ifdef::doctype-manpage[]
+
+[macros]
+(?su)[\\]?(?P<name>man_link):(?P<target>\S*?)\[(?P<attrlist>.*?)\]=
+
+ifdef::backend-docbook[]
+[man_link-inlinemacro]
+{0%{target}}
+{0#<citerefentry>}
+{0#<refentrytitle>{target}</refentrytitle><manvolnum>{0}</manvolnum>}
+{0#</citerefentry>}
+endif::backend-docbook[]
+
+ifdef::basebackend-html[]
+[man_link-inlinemacro]
+<a href="{target}.html">{target}{0?({0})}</a>
+endif::basebackend-html[]
+
+endif::doctype-manpage[]
diff --git a/chromium/third_party/crashpad/crashpad/doc/support/asciidoc.css b/chromium/third_party/crashpad/crashpad/doc/support/asciidoc.css
new file mode 100644
index 00000000000..9808c216cc5
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/doc/support/asciidoc.css
@@ -0,0 +1,20 @@
+/* Copyright 2015 The Crashpad Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License. */
+
+/* The default AsciiDoc asciidoc.css specifies fuchsia as the visited link
+ * color. This has a dated appearance. Replace it with blue, the same color used
+ * for unvisited links. */
+a:visited {
+ color: blue;
+}
diff --git a/chromium/third_party/crashpad/crashpad/doc/support/crashpad.doxy b/chromium/third_party/crashpad/crashpad/doc/support/crashpad.doxy
new file mode 100644
index 00000000000..16f9ed57e24
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/doc/support/crashpad.doxy
@@ -0,0 +1,2369 @@
+# Doxyfile 1.8.9.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = "Crashpad"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = out/doc/doxygen
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH = . \
+ compat/mac \
+ compat/non_win
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = YES
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = YES
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
+
+INPUT =
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
+
+FILE_PATTERNS = *.c \
+ *.cc \
+ *.h \
+ *.m \
+ *.mm
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE = third_party
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS = out*
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 0
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sf.net) file that captures the
+# structure of the code including all documentation. Note that this feature is
+# still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = YES
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED = DOXYGEN \
+ __attribute__(x)=
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
diff --git a/chromium/third_party/crashpad/crashpad/doc/support/crashpad.doxy.h b/chromium/third_party/crashpad/crashpad/doc/support/crashpad.doxy.h
new file mode 100644
index 00000000000..5bfefedd2ad
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/doc/support/crashpad.doxy.h
@@ -0,0 +1,24 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#error This file is not intended to be #included.
+
+//! \namespace crashpad
+//! \brief The main namespace.
+
+//! \namespace crashpad::internal
+//! \brief The internal namespace, not for public use.
+
+//! \namespace crashpad::test
+//! \brief The testing namespace, for use in test code only.
diff --git a/chromium/third_party/crashpad/crashpad/doc/support/generate_asciidoc.sh b/chromium/third_party/crashpad/crashpad/doc/support/generate_asciidoc.sh
new file mode 100755
index 00000000000..ea5bd4671d4
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/doc/support/generate_asciidoc.sh
@@ -0,0 +1,129 @@
+#!/bin/bash
+
+# Copyright 2015 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e
+
+# Generating AsciiDoc documentation requires AsciiDoc,
+# http://www.methods.co.nz/asciidoc/. For “man” and PDF output, a DocBook
+# toolchain including docbook-xml and docbook-xsl is also required.
+
+# Run from the Crashpad project root directory.
+cd "$(dirname "${0}")/../.."
+
+output_dir=out/doc
+
+rm -rf \
+ "${output_dir}/doc" \
+ "${output_dir}/man"
+mkdir -p \
+ "${output_dir}/doc/html" \
+ "${output_dir}/man/html" \
+ "${output_dir}/man/man"
+
+# Some extensions of command-line tools behave differently on different systems.
+# $sed_ext should be a sed invocation that enables extended regular expressions.
+# $date_time_t should be a date invocation that causes it to print the date and
+# time corresponding to a time_t string that immediately follows it.
+uname_s="$(uname -s)"
+case "${uname_s}" in
+ Darwin)
+ sed_ext="sed -E"
+ date_time_t="date -r"
+ ;;
+ Linux)
+ sed_ext="sed -r"
+ date_time_t="date -d@"
+ ;;
+ *)
+ echo "${0}: unknown operating system" >& 2
+ exit 1
+ ;;
+esac
+
+# Get the version from package.h.
+version=$(${sed_ext} -n -e 's/^#define PACKAGE_VERSION "(.*)"$/\1/p' package.h)
+
+generate() {
+ input="$1"
+ type="$2"
+
+ case "${type}" in
+ doc)
+ doctype="article"
+ ;;
+ man)
+ doctype="manpage"
+ ;;
+ *)
+ echo "${0}: unknown type ${type}" >& 2
+ exit 1
+ ;;
+ esac
+
+ echo "${input}"
+
+ base=$(${sed_ext} -e 's%^.*/([^/]+)\.ad$%\1%' <<< "${input}")
+
+ # Get the last-modified date of $input according to Git, in UTC.
+ git_time_t="$(git log -1 --format=%at "${input}")"
+ git_date="$(LC_ALL=C ${date_time_t}"${git_time_t}" -u '+%B %-d, %Y')"
+
+ # Create HTML output.
+ asciidoc \
+ --attribute mansource=Crashpad \
+ --attribute manversion="${version}" \
+ --attribute manmanual="Crashpad Manual" \
+ --attribute git_date="${git_date}" \
+ --conf-file doc/support/asciidoc.conf \
+ --doctype "${doctype}" \
+ --backend html5 \
+ --attribute stylesheet="${PWD}/doc/support/asciidoc.css" \
+ --out-file "${output_dir}/${type}/html/${base}.html" \
+ "${input}"
+
+ if [[ "${type}" = "man" ]]; then
+ # Create “man” output.
+ #
+ # AsciiDoc 8.6.9 produces harmless incorrect warnings each time this is run:
+ # “a2x: WARNING: --destination-dir option is only applicable to HTML based
+ # outputs”. https://github.com/asciidoc/asciidoc/issues/44
+ a2x \
+ --attribute mansource=Crashpad \
+ --attribute manversion="${version}" \
+ --attribute manmanual="Crashpad Manual" \
+ --attribute git_date="${git_date}" \
+ --asciidoc-opts=--conf-file=doc/support/asciidoc.conf \
+ --doctype "${doctype}" \
+ --format manpage \
+ --destination-dir "${output_dir}/${type}/man" \
+ "${input}"
+ fi
+
+ # For PDF output, use an a2x command like the one above, with these options:
+ # --format pdf --fop --destination-dir "${output_dir}/${type}/pdf"
+}
+
+for input in \
+ doc/*.ad; do
+ generate "${input}" "doc"
+done
+
+for input in \
+ handler/mac/crashpad_handler.ad \
+ tools/*.ad \
+ tools/mac/*.ad; do
+ generate "${input}" "man"
+done
diff --git a/chromium/third_party/crashpad/crashpad/doc/support/generate_doxygen.sh b/chromium/third_party/crashpad/crashpad/doc/support/generate_doxygen.sh
new file mode 100755
index 00000000000..ba565e7bc46
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/doc/support/generate_doxygen.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# Copyright 2015 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e
+
+# Generating Doxygen documentation requires Doxygen, http://www.doxygen.org/.
+
+# Run from the Crashpad project root directory.
+cd "$(dirname "${0}")/../.."
+
+output_dir=out/doc/doxygen
+
+rm -rf "${output_dir}"
+mkdir -p "${output_dir}"
+
+doxygen doc/support/crashpad.doxy
diff --git a/chromium/third_party/crashpad/crashpad/doc/support/man_footer.ad b/chromium/third_party/crashpad/crashpad/doc/support/man_footer.ad
new file mode 100644
index 00000000000..10853855ff3
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/doc/support/man_footer.ad
@@ -0,0 +1,40 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+== Resources
+
+Crashpad home page: https://crashpad.googlecode.com/.
+
+Report bugs at https://code.google.com/p/crashpad/issues/entry.
+
+== Copyright
+
+Copyright 2014
+https://chromium.googlesource.com/crashpad/crashpad/+/master/AUTHORS[The
+Crashpad Authors].
+
+== License
+
+Licensed under the Apache License, Version 2.0 (the “License”);
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+[subs="macros"]
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an “AS IS” BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/chromium/third_party/crashpad/crashpad/handler/handler.gyp b/chromium/third_party/crashpad/crashpad/handler/handler.gyp
new file mode 100644
index 00000000000..082eebea3f8
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/handler/handler.gyp
@@ -0,0 +1,75 @@
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'includes': [
+ '../build/crashpad.gypi',
+ '../build/crashpad_in_chromium.gypi',
+ ],
+ 'conditions': [
+ ['OS=="mac"', {
+ 'targets': [
+ {
+ 'target_name': 'crashpad_handler',
+ 'type': 'executable',
+ 'dependencies': [
+ '../client/client.gyp:crashpad_client',
+ '../compat/compat.gyp:crashpad_compat',
+ '../minidump/minidump.gyp:crashpad_minidump',
+ '../snapshot/snapshot.gyp:crashpad_snapshot',
+ '../third_party/mini_chromium/mini_chromium.gyp:base',
+ '../tools/tools.gyp:crashpad_tool_support',
+ '../util/util.gyp:crashpad_util',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'mac/crash_report_exception_handler.cc',
+ 'mac/crash_report_exception_handler.h',
+ 'mac/crash_report_upload_thread.cc',
+ 'mac/crash_report_upload_thread.h',
+ 'mac/exception_handler_server.cc',
+ 'mac/exception_handler_server.h',
+ 'mac/main.cc',
+ ],
+
+ # In an in-Chromium build with component=shared_library,
+ # crashpad_handler will depend on shared libraries such as
+ # libbase.dylib located in out/{Debug,Release} via the @rpath
+ # mechanism. When crashpad_handler is copied to its home deep inside
+ # the Chromium app bundle, it needs to have an LC_RPATH command
+ # pointing back to the directory containing these dependency
+ # libraries.
+ 'variables': {
+ 'component%': 'static_library',
+ },
+ 'conditions': [
+ ['crashpad_in_chromium!=0 and component=="shared_library"', {
+ 'xcode_settings': {
+ 'LD_RUNPATH_SEARCH_PATHS': [ # -Wl,-rpath
+ # Get back from
+ # Chromium.app/Contents/Versions/V/Framework.framework/Helpers
+ '@loader_path/../../../../../..',
+ ],
+ },
+ }],
+ ],
+ },
+ ],
+ }, {
+ 'targets': [],
+ }],
+ ],
+}
diff --git a/chromium/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.cc b/chromium/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.cc
new file mode 100644
index 00000000000..fced33c8459
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.cc
@@ -0,0 +1,261 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "handler/mac/crash_report_exception_handler.h"
+
+#include <servers/bootstrap.h>
+
+#include <vector>
+
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "base/strings/stringprintf.h"
+#include "client/settings.h"
+#include "minidump/minidump_file_writer.h"
+#include "snapshot/crashpad_info_client_options.h"
+#include "snapshot/mac/process_snapshot_mac.h"
+#include "util/file/file_writer.h"
+#include "util/mach/exc_client_variants.h"
+#include "util/mach/exception_behaviors.h"
+#include "util/mach/exception_types.h"
+#include "util/mach/mach_extensions.h"
+#include "util/mach/mach_message.h"
+#include "util/mach/scoped_task_suspend.h"
+#include "util/mach/symbolic_constants_mach.h"
+#include "util/misc/tri_state.h"
+#include "util/misc/uuid.h"
+
+namespace crashpad {
+
+CrashReportExceptionHandler::CrashReportExceptionHandler(
+ CrashReportDatabase* database,
+ CrashReportUploadThread* upload_thread,
+ const std::map<std::string, std::string>* process_annotations)
+ : database_(database),
+ upload_thread_(upload_thread),
+ process_annotations_(process_annotations) {
+}
+
+CrashReportExceptionHandler::~CrashReportExceptionHandler() {
+}
+
+kern_return_t CrashReportExceptionHandler::CatchMachException(
+ exception_behavior_t behavior,
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_complex_request) {
+ *destroy_complex_request = true;
+
+ // The expected behavior is EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES,
+ // but it’s possible to deal with any exception behavior as long as it
+ // carries identity information (valid thread and task ports).
+ if (!ExceptionBehaviorHasIdentity(behavior)) {
+ LOG(ERROR) << base::StringPrintf(
+ "unexpected exception behavior %s, rejecting",
+ ExceptionBehaviorToString(
+ behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str());
+ return KERN_FAILURE;
+ } else if (behavior != (EXCEPTION_STATE_IDENTITY | kMachExceptionCodes)) {
+ LOG(WARNING) << base::StringPrintf(
+ "unexpected exception behavior %s, proceeding",
+ ExceptionBehaviorToString(
+ behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str());
+ }
+
+ if (task == mach_task_self()) {
+ LOG(ERROR) << "cannot suspend myself";
+ return KERN_FAILURE;
+ }
+
+ ScopedTaskSuspend suspend(task);
+
+ ProcessSnapshotMac process_snapshot;
+ if (!process_snapshot.Initialize(task)) {
+ return KERN_FAILURE;
+ }
+
+ // Check for suspicious message sources. A suspicious exception message comes
+ // from a source other than the kernel or the process that the exception
+ // purportedly occurred in.
+ //
+ // TODO(mark): Consider exceptions outside of the range (0, 32) from the
+ // kernel to be suspicious, and exceptions other than kMachExceptionSimulated
+ // from the process itself to be suspicious.
+ const pid_t pid = process_snapshot.ProcessID();
+ pid_t audit_pid = AuditPIDFromMachMessageTrailer(trailer);
+ if (audit_pid != -1 && audit_pid != 0) {
+ if (audit_pid != pid) {
+ LOG(WARNING) << "exception for pid " << pid << " sent by pid "
+ << audit_pid;
+ }
+ }
+
+ if (IsExceptionNonfatalResource(exception, code[0], pid)) {
+ // Swallow non-fatal resource exceptions.
+ //
+ // Normally, all EXC_RESOURCE exceptions go to the host-level EXC_RESOURCE
+ // handler, com.apple.ReportCrash.root, which invokes spindump to handle
+ // them. These non-fatal exceptions are never user-visible and are not
+ // currently of interest to Crashpad. Returning success here gets the
+ // process going again quickly, without generating a crash report.
+ //
+ // Alternatively, this could return KERN_FAILURE to let the exception go to
+ // the host-level handler, but there doesn’t seem to be much value in doing
+ // so.
+ ExcServerCopyState(
+ behavior, old_state, old_state_count, new_state, new_state_count);
+ return ExcServerSuccessfulReturnValue(behavior, false);
+ }
+
+ CrashpadInfoClientOptions client_options;
+ process_snapshot.GetCrashpadOptions(&client_options);
+
+ if (client_options.crashpad_handler_behavior != TriState::kDisabled) {
+ if (!process_snapshot.InitializeException(behavior,
+ thread,
+ exception,
+ code,
+ code_count,
+ *flavor,
+ old_state,
+ old_state_count)) {
+ return KERN_FAILURE;
+ }
+
+ UUID client_id;
+ Settings* const settings = database_->GetSettings();
+ if (settings) {
+ // If GetSettings() or GetClientID() fails, something else will log a
+ // message and client_id will be left at its default value, all zeroes,
+ // which is appropriate.
+ settings->GetClientID(&client_id);
+ }
+
+ process_snapshot.SetClientID(client_id);
+ process_snapshot.SetAnnotationsSimpleMap(*process_annotations_);
+
+ CrashReportDatabase::NewReport* new_report;
+ CrashReportDatabase::OperationStatus database_status =
+ database_->PrepareNewCrashReport(&new_report);
+ if (database_status != CrashReportDatabase::kNoError) {
+ return KERN_FAILURE;
+ }
+
+ process_snapshot.SetReportID(new_report->uuid);
+
+ CrashReportDatabase::CallErrorWritingCrashReport
+ call_error_writing_crash_report(database_, new_report);
+
+ WeakFileHandleFileWriter file_writer(new_report->handle);
+
+ MinidumpFileWriter minidump;
+ minidump.InitializeFromSnapshot(&process_snapshot);
+ if (!minidump.WriteEverything(&file_writer)) {
+ return KERN_FAILURE;
+ }
+
+ call_error_writing_crash_report.Disarm();
+
+ UUID uuid;
+ database_status = database_->FinishedWritingCrashReport(new_report, &uuid);
+ if (database_status != CrashReportDatabase::kNoError) {
+ return KERN_FAILURE;
+ }
+
+ upload_thread_->ReportPending();
+ }
+
+ bool forwarded = false;
+ if (client_options.system_crash_reporter_forwarding != TriState::kDisabled &&
+ (exception == EXC_CRASH ||
+ exception == EXC_RESOURCE ||
+ exception == EXC_GUARD)) {
+ // Don’t forward simulated exceptions such as kMachExceptionSimulated to the
+ // system crash reporter. Only forward the types of exceptions that it would
+ // receive under normal conditions. Although the system crash reporter is
+ // able to deal with other exceptions including simulated ones, forwarding
+ // them to the system crash reporter could present the system’s crash UI for
+ // processes that haven’t actually crashed, and could result in reports not
+ // actually associated with crashes being sent to the operating system
+ // vendor.
+ //
+ // Note that normally, EXC_RESOURCE and EXC_GUARD exceptions are sent to the
+ // system-level com.apple.ReportCrash.Root job, and not to the user-level
+ // job that they are forwarded to here.
+ mach_port_t system_crash_reporter_port;
+ const char kSystemCrashReporterServiceName[] = "com.apple.ReportCrash";
+ kern_return_t kr = bootstrap_look_up(bootstrap_port,
+ kSystemCrashReporterServiceName,
+ &system_crash_reporter_port);
+ if (kr != BOOTSTRAP_SUCCESS) {
+ BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up "
+ << kSystemCrashReporterServiceName;
+ } else {
+ // Make copies of mutable out parameters so that the system crash reporter
+ // can’t influence the state returned by this method.
+ thread_state_flavor_t flavor_forward = *flavor;
+ mach_msg_type_number_t new_state_forward_count = *new_state_count;
+ std::vector<natural_t> new_state_forward(
+ new_state, new_state + new_state_forward_count);
+
+ // The system crash reporter requires the behavior to be
+ // EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES. It uses the identity
+ // parameters but doesn’t appear to use the state parameters, including
+ // |flavor|, and doesn’t care if they are 0 or invalid. As long as an
+ // identity is available (checked above), any other exception behavior is
+ // converted to what the system crash reporter wants, with the caveat that
+ // problems may arise if the state wasn’t available and the system crash
+ // reporter changes in the future to use it. However, normally, the state
+ // will be available.
+ kr = UniversalExceptionRaise(
+ EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES,
+ system_crash_reporter_port,
+ thread,
+ task,
+ exception,
+ code,
+ code_count,
+ &flavor_forward,
+ old_state,
+ old_state_count,
+ new_state_forward_count ? &new_state_forward[0] : nullptr,
+ &new_state_forward_count);
+ if (kr == KERN_SUCCESS) {
+ forwarded = true;
+ } else {
+ MACH_LOG(WARNING, kr)
+ << "UniversalExceptionRaise " << kSystemCrashReporterServiceName;
+ }
+ }
+ }
+
+ if (!forwarded) {
+ ExcServerCopyState(
+ behavior, old_state, old_state_count, new_state, new_state_count);
+ }
+
+ return ExcServerSuccessfulReturnValue(behavior, false);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.h b/chromium/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.h
new file mode 100644
index 00000000000..67a523a90c9
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/handler/mac/crash_report_exception_handler.h
@@ -0,0 +1,86 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_HANDLER_MAC_CRASH_REPORT_EXCEPTION_HANDLER_H_
+#define CRASHPAD_HANDLER_MAC_CRASH_REPORT_EXCEPTION_HANDLER_H_
+
+#include <mach/mach.h>
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "client/crash_report_database.h"
+#include "handler/mac/crash_report_upload_thread.h"
+#include "util/mach/exc_server_variants.h"
+
+namespace crashpad {
+
+//! \brief An exception handler that writes crash reports for exception messages
+//! to a CrashReportDatabase.
+class CrashReportExceptionHandler : public UniversalMachExcServer::Interface {
+ public:
+ //! \brief Creates a new object that will store crash reports in \a database.
+ //!
+ //! \param[in] database The database to store crash reports in. Weak.
+ //! \param[in] upload_thread The upload thread to notify when a new crash
+ //! report is written into \a database.
+ //! \param[in] process_annotations A map of annotations to insert as
+ //! process-level annotations into each crash report that is written. Do
+ //! not confuse this with module-level annotations, which are under the
+ //! control of the crashing process, and are used to implement Chrome’s
+ //! “crash keys.” Process-level annotations are those that are beyond the
+ //! control of the crashing process, which must reliably be set even if
+ //! the process crashes before it’s able to establish its own annotations.
+ //! To interoperate with Breakpad servers, the recommended practice is to
+ //! specify values for the `"prod"` and `"ver"` keys as process
+ //! annotations.
+ CrashReportExceptionHandler(
+ CrashReportDatabase* database,
+ CrashReportUploadThread* upload_thread,
+ const std::map<std::string, std::string>* process_annotations);
+
+ ~CrashReportExceptionHandler();
+
+ // UniversalMachExcServer::Interface:
+
+ //! \brief Processes an exception message by writing a crash report to this
+ //! object’s CrashReportDatabase.
+ kern_return_t CatchMachException(
+ exception_behavior_t behavior,
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_complex_request) override;
+
+ private:
+ CrashReportDatabase* database_; // weak
+ CrashReportUploadThread* upload_thread_; // weak
+ const std::map<std::string, std::string>* process_annotations_; // weak
+
+ DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_HANDLER_MAC_CRASH_REPORT_EXCEPTION_HANDLER_H_
diff --git a/chromium/third_party/crashpad/crashpad/handler/mac/crash_report_upload_thread.cc b/chromium/third_party/crashpad/crashpad/handler/mac/crash_report_upload_thread.cc
new file mode 100644
index 00000000000..e266b7f764f
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/handler/mac/crash_report_upload_thread.cc
@@ -0,0 +1,366 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "handler/mac/crash_report_upload_thread.h"
+
+#include <errno.h>
+#include <time.h>
+
+#include <map>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "client/settings.h"
+#include "snapshot/minidump/process_snapshot_minidump.h"
+#include "snapshot/module_snapshot.h"
+#include "util/file/file_reader.h"
+#include "util/misc/uuid.h"
+#include "util/net/http_body.h"
+#include "util/net/http_multipart_builder.h"
+#include "util/net/http_transport.h"
+#include "util/stdlib/map_insert.h"
+
+namespace crashpad {
+
+namespace {
+
+void InsertOrReplaceMapEntry(std::map<std::string, std::string>* map,
+ const std::string& key,
+ const std::string& value) {
+ std::string old_value;
+ if (!MapInsertOrReplace(map, key, value, &old_value)) {
+ LOG(WARNING) << "duplicate key " << key << ", discarding value "
+ << old_value;
+ }
+}
+
+// Given a minidump file readable by |minidump_file_reader|, returns a map of
+// key-value pairs to use as HTTP form parameters for upload to a Breakpad
+// server. The map is built by combining the process simple annotations map with
+// each module’s simple annotations map. In the case of duplicate keys, the map
+// will retain the first value found for any key, and will log a warning about
+// discarded values. Each module’s annotations vector is also examined and built
+// into a single string value, with distinct elements separated by newlines, and
+// stored at the key named “list_annotations”, which supersedes any other key
+// found by that name. The client ID stored in the minidump is converted to
+// a string and stored at the key named “guid”, which supersedes any other key
+// found by that name.
+//
+// In the event of an error reading the minidump file, a message will be logged.
+std::map<std::string, std::string> BreakpadHTTPFormParametersFromMinidump(
+ FileReader* minidump_file_reader) {
+ ProcessSnapshotMinidump minidump_process_snapshot;
+ if (!minidump_process_snapshot.Initialize(minidump_file_reader)) {
+ return std::map<std::string, std::string>();
+ }
+
+ std::map<std::string, std::string> parameters =
+ minidump_process_snapshot.AnnotationsSimpleMap();
+
+ std::string list_annotations;
+ for (const ModuleSnapshot* module : minidump_process_snapshot.Modules()) {
+ for (const auto& kv : module->AnnotationsSimpleMap()) {
+ if (!parameters.insert(kv).second) {
+ LOG(WARNING) << "duplicate key " << kv.first << ", discarding value "
+ << kv.second;
+ }
+ }
+
+ for (std::string annotation : module->AnnotationsVector()) {
+ list_annotations.append(annotation);
+ list_annotations.append("\n");
+ }
+ }
+
+ if (!list_annotations.empty()) {
+ // Remove the final newline character.
+ list_annotations.resize(list_annotations.size() - 1);
+
+ InsertOrReplaceMapEntry(&parameters, "list_annotations", list_annotations);
+ }
+
+ UUID client_id;
+ minidump_process_snapshot.ClientID(&client_id);
+ InsertOrReplaceMapEntry(&parameters, "guid", client_id.ToString());
+
+ return parameters;
+}
+
+// Calls CrashReportDatabase::RecordUploadAttempt() with |successful| set to
+// false upon destruction unless disarmed by calling Fire() or Disarm(). Fire()
+// triggers an immediate call. Armed upon construction.
+class CallRecordUploadAttempt {
+ public:
+ CallRecordUploadAttempt(CrashReportDatabase* database,
+ const CrashReportDatabase::Report* report)
+ : database_(database),
+ report_(report) {
+ }
+
+ ~CallRecordUploadAttempt() {
+ Fire();
+ }
+
+ void Fire() {
+ if (report_) {
+ database_->RecordUploadAttempt(report_, false, std::string());
+ }
+
+ Disarm();
+ }
+
+ void Disarm() {
+ report_ = nullptr;
+ }
+
+ private:
+ CrashReportDatabase* database_; // weak
+ const CrashReportDatabase::Report* report_; // weak
+
+ DISALLOW_COPY_AND_ASSIGN(CallRecordUploadAttempt);
+};
+
+} // namespace
+
+CrashReportUploadThread::CrashReportUploadThread(CrashReportDatabase* database,
+ const std::string& url)
+ : url_(url),
+ database_(database),
+ semaphore_(0),
+ thread_(0),
+ running_(false) {
+}
+
+CrashReportUploadThread::~CrashReportUploadThread() {
+ DCHECK(!running_);
+ DCHECK(!thread_);
+}
+
+void CrashReportUploadThread::Start() {
+ DCHECK(!running_);
+ DCHECK(!thread_);
+
+ running_ = true;
+ if ((errno = pthread_create(&thread_, nullptr, RunThreadMain, this)) != 0) {
+ PLOG(ERROR) << "pthread_create";
+ DCHECK(false);
+ running_ = false;
+ }
+}
+
+void CrashReportUploadThread::Stop() {
+ DCHECK(running_);
+ DCHECK(thread_);
+
+ if (!running_) {
+ return;
+ }
+
+ running_ = false;
+ semaphore_.Signal();
+
+ if ((errno = pthread_join(thread_, nullptr)) != 0) {
+ PLOG(ERROR) << "pthread_join";
+ DCHECK(false);
+ }
+
+ thread_ = 0;
+}
+
+void CrashReportUploadThread::ReportPending() {
+ semaphore_.Signal();
+}
+
+void CrashReportUploadThread::ThreadMain() {
+ while (running_) {
+ ProcessPendingReports();
+
+ // Check for pending reports every 15 minutes, even in the absence of a
+ // signal from the handler thread. This allows for failed uploads to be
+ // retried periodically, and for pending reports written by other processes
+ // to be recognized.
+ semaphore_.TimedWait(15 * 60);
+ }
+}
+
+void CrashReportUploadThread::ProcessPendingReports() {
+ std::vector<CrashReportDatabase::Report> reports;
+ if (database_->GetPendingReports(&reports) != CrashReportDatabase::kNoError) {
+ // The database is sick. It might be prudent to stop trying to poke it from
+ // this thread by abandoning the thread altogether. On the other hand, if
+ // the problem is transient, it might be possible to talk to it again on the
+ // next pass. For now, take the latter approach.
+ return;
+ }
+
+ for (const CrashReportDatabase::Report& report : reports) {
+ ProcessPendingReport(report);
+
+ // Respect Stop() being called after at least one attempt to process a
+ // report.
+ if (!running_) {
+ return;
+ }
+ }
+}
+
+void CrashReportUploadThread::ProcessPendingReport(
+ const CrashReportDatabase::Report& report) {
+ Settings* const settings = database_->GetSettings();
+
+ bool uploads_enabled;
+ if (!settings->GetUploadsEnabled(&uploads_enabled) ||
+ !uploads_enabled ||
+ url_.empty()) {
+ // If the upload-enabled state can’t be determined, uploads are disabled, or
+ // there’s no URL to upload to, don’t attempt to upload the new report.
+ database_->SkipReportUpload(report.uuid);
+ return;
+ }
+
+ // This currently implements very simplistic rate-limiting, compatible with
+ // the Breakpad client, where the strategy is to permit one upload attempt per
+ // hour, and retire reports that would exceed this limit or for which the
+ // upload fails on the first attempt.
+ //
+ // TODO(mark): Provide a proper rate-limiting strategy and allow for failed
+ // upload attempts to be retried.
+ time_t last_upload_attempt_time;
+ if (settings->GetLastUploadAttemptTime(&last_upload_attempt_time)) {
+ time_t now = time(nullptr);
+ if (now >= last_upload_attempt_time) {
+ // If the most recent upload attempt occurred within the past hour, don’t
+ // attempt to upload the new report. If it happened longer ago, attempt to
+ // upload the report.
+ const int kUploadAttemptIntervalSeconds = 60 * 60; // 1 hour
+ if (now - last_upload_attempt_time < kUploadAttemptIntervalSeconds) {
+ database_->SkipReportUpload(report.uuid);
+ return;
+ }
+ } else {
+ // The most recent upload attempt purportedly occurred in the future. If
+ // it “happened” at least one day in the future, assume that the last
+ // upload attempt time is bogus, and attempt to upload the report. If the
+ // most recent upload time is in the future but within one day, accept it
+ // and don’t attempt to upload the report.
+ const int kBackwardsClockTolerance = 60 * 60 * 24; // 1 day
+ if (last_upload_attempt_time - now < kBackwardsClockTolerance) {
+ database_->SkipReportUpload(report.uuid);
+ return;
+ }
+ }
+ }
+
+ const CrashReportDatabase::Report* upload_report;
+ CrashReportDatabase::OperationStatus status =
+ database_->GetReportForUploading(report.uuid, &upload_report);
+ switch (status) {
+ case CrashReportDatabase::kNoError:
+ break;
+
+ case CrashReportDatabase::kBusyError:
+ return;
+
+ case CrashReportDatabase::kReportNotFound:
+ case CrashReportDatabase::kFileSystemError:
+ case CrashReportDatabase::kDatabaseError:
+ // In these cases, SkipReportUpload() might not work either, but it’s best
+ // to at least try to get the report out of the way.
+ database_->SkipReportUpload(report.uuid);
+ return;
+ }
+
+ CallRecordUploadAttempt call_record_upload_attempt(database_, upload_report);
+
+ std::string response_body;
+ UploadResult upload_result = UploadReport(upload_report, &response_body);
+ switch (upload_result) {
+ case UploadResult::kSuccess:
+ call_record_upload_attempt.Disarm();
+ database_->RecordUploadAttempt(upload_report, true, response_body);
+ break;
+ case UploadResult::kPermanentFailure:
+ case UploadResult::kRetry:
+ call_record_upload_attempt.Fire();
+
+ // TODO(mark): Deal with retries properly: don’t call SkipReportUplaod()
+ // if the result was kRetry and the report hasn’t already been retried
+ // too many times.
+ database_->SkipReportUpload(report.uuid);
+ break;
+ }
+}
+
+CrashReportUploadThread::UploadResult CrashReportUploadThread::UploadReport(
+ const CrashReportDatabase::Report* report,
+ std::string* response_body) {
+ std::map<std::string, std::string> parameters;
+
+ {
+ FileReader minidump_file_reader;
+ if (!minidump_file_reader.Open(report->file_path)) {
+ // If the minidump file can’t be opened, all hope is lost.
+ return UploadResult::kPermanentFailure;
+ }
+
+ // If the minidump file could be opened, ignore any errors that might occur
+ // when attempting to interpret it. This may result in its being uploaded
+ // with few or no parameters, but as long as there’s a dump file, the server
+ // can decide what to do with it.
+ parameters = BreakpadHTTPFormParametersFromMinidump(&minidump_file_reader);
+ }
+
+ HTTPMultipartBuilder http_multipart_builder;
+
+ const char kMinidumpKey[] = "upload_file_minidump";
+
+ for (const auto& kv : parameters) {
+ if (kv.first == kMinidumpKey) {
+ LOG(WARNING) << "reserved key " << kv.first << ", discarding value "
+ << kv.second;
+ } else {
+ http_multipart_builder.SetFormData(kv.first, kv.second);
+ }
+ }
+
+ http_multipart_builder.SetFileAttachment(kMinidumpKey,
+ report->file_path.BaseName().value(),
+ report->file_path,
+ "application/octet-stream");
+
+ scoped_ptr<HTTPTransport> http_transport(HTTPTransport::Create());
+ http_transport->SetURL(url_);
+ HTTPHeaders::value_type content_type =
+ http_multipart_builder.GetContentType();
+ http_transport->SetHeader(content_type.first, content_type.second);
+ http_transport->SetBodyStream(http_multipart_builder.GetBodyStream().Pass());
+ // TODO(mark): The timeout should be configurable by the client.
+ http_transport->SetTimeout(60.0); // 1 minute.
+
+ if (!http_transport->ExecuteSynchronously(response_body)) {
+ return UploadResult::kRetry;
+ }
+
+ return UploadResult::kSuccess;
+}
+
+// static
+void* CrashReportUploadThread::RunThreadMain(void* arg) {
+ CrashReportUploadThread* self = static_cast<CrashReportUploadThread*>(arg);
+ self->ThreadMain();
+ return nullptr;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/handler/mac/crash_report_upload_thread.h b/chromium/third_party/crashpad/crashpad/handler/mac/crash_report_upload_thread.h
new file mode 100644
index 00000000000..6c9be5bf8c3
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/handler/mac/crash_report_upload_thread.h
@@ -0,0 +1,152 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_HANDLER_MAC_CRASH_REPORT_UPLOAD_THREAD_H_
+#define CRASHPAD_HANDLER_MAC_CRASH_REPORT_UPLOAD_THREAD_H_
+
+#include "base/basictypes.h"
+
+#include <pthread.h>
+
+#include <string>
+
+#include "client/crash_report_database.h"
+#include "util/synchronization/semaphore.h"
+
+namespace crashpad {
+
+//! \brief A thread that processes pending crash reports in a
+//! CrashReportDatabase by uploading them or marking them as completed
+//! without upload, as desired.
+//!
+//! A producer of crash reports should notify an object of this class that a new
+//! report has been added to the database by calling ReportPending().
+//!
+//! Independently of being triggered by ReportPending(), objects of this class
+//! periodically examine the database for pending reports. This allows failed
+//! upload attempts for reports left in the pending state to be retried. It also
+//! catches reports that are added without a ReportPending() signal being
+//! caught. This may happen if crash reports are added to the database by other
+//! processes.
+class CrashReportUploadThread {
+ public:
+ //! \brief Constructs a new object.
+ //!
+ //! \param[in] database The database to upload crash reports from.
+ //! \param[in] url The URL of the server to upload crash reports to.
+ CrashReportUploadThread(CrashReportDatabase* database,
+ const std::string& url);
+ ~CrashReportUploadThread();
+
+ //! \brief Starts a dedicated upload thread, which executes ThreadMain().
+ //!
+ //! This method may only be be called on a newly-constructed object or after
+ //! a call to Stop().
+ void Start();
+
+ //! \brief Stops the upload thread.
+ //!
+ //! The upload thread will terminate after completing whatever task it is
+ //! performing. If it is not performing any task, it will terminate
+ //! immediately. This method blocks while waiting for the upload thread to
+ //! terminate.
+ //!
+ //! This method must only be called after Start(). If Start() has been called,
+ //! this method must be called before destroying an object of this class.
+ //!
+ //! This method may be called from any thread other than the upload thread.
+ //! It is expected to only be called from the same thread that called Start().
+ void Stop();
+
+ //! \brief Informs the upload thread that a new pending report has been added
+ //! to the database.
+ //!
+ //! This method may be called from any thread.
+ void ReportPending();
+
+ private:
+ //! \brief The result code from UploadReport().
+ enum class UploadResult {
+ //! \brief The crash report was uploaded successfully.
+ kSuccess,
+
+ //! \brief The crash report upload failed in such a way that recovery is
+ //! impossible.
+ //!
+ //! No further upload attempts should be made for the report.
+ kPermanentFailure,
+
+ //! \brief The crash report upload failed, but it might succeed again if
+ //! retried in the future.
+ //!
+ //! If the report has not already been retried too many times, the caller
+ //! may arrange to call UploadReport() for the report again in the future,
+ //! after a suitable delay.
+ kRetry,
+ };
+
+ //! \brief Calls ProcessPendingReports() in response to ReportPending() having
+ //! been called on any thread, as well as periodically on a timer.
+ void ThreadMain();
+
+ //! \brief Obtains all pending reports from the database, and calls
+ //! ProcessPendingReport() to process each one.
+ void ProcessPendingReports();
+
+ //! \brief Processes a single pending report from the database.
+ //!
+ //! \param[in] report The crash report to process.
+ //!
+ //! If report upload is enabled, this method attempts to upload \a report by
+ //! calling UplaodReport(). If the upload is successful, the report will be
+ //! marked as “completed” in the database. If the upload fails and more
+ //! retries are desired, the report’s upload-attempt count and
+ //! last-upload-attempt time will be updated in the database and it will
+ //! remain in the “pending” state. If the upload fails and no more retries are
+ //! desired, or report upload is disabled, it will be marked as “completed” in
+ //! the database without ever having been uploaded.
+ void ProcessPendingReport(const CrashReportDatabase::Report& report);
+
+ //! \brief Attempts to upload a crash report.
+ //!
+ //! \param[in] report The report to upload. The caller is responsible for
+ //! calling CrashReportDatabase::GetReportForUploading() before calling
+ //! this method, and for calling
+ //! CrashReportDatabase::RecordUploadAttempt() after calling this method.
+ //! \param[out] response_body If the upload attempt is successful, this will
+ //! be set to the response body sent by the server. Breakpad-type servers
+ //! provide the crash ID assigned by the server in the response body.
+ //!
+ //! \return A member of UploadResult indicating the result of the upload
+ //! attempt.
+ UploadResult UploadReport(const CrashReportDatabase::Report* report,
+ std::string* response_body);
+
+ //! \brief Cals ThreadMain().
+ //!
+ //! \param[in] arg A pointer to the object on which to invoke ThreadMain().
+ //!
+ //! \return `nullptr`.
+ static void* RunThreadMain(void* arg);
+
+ std::string url_;
+ CrashReportDatabase* database_; // weak
+ Semaphore semaphore_; // TODO(mark): Use a condition variable instead?
+ pthread_t thread_;
+ bool running_;
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_HANDLER_MAC_CRASH_REPORT_UPLOAD_THREAD_H_
diff --git a/chromium/third_party/crashpad/crashpad/handler/mac/crashpad_handler.ad b/chromium/third_party/crashpad/crashpad/handler/mac/crashpad_handler.ad
new file mode 100644
index 00000000000..b53d0977114
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/handler/mac/crashpad_handler.ad
@@ -0,0 +1,108 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+:doctype: manpage
+
+= crashpad_handler(8)
+
+== Name
+
+crashpad_handler - Crashpad’s exception handler server
+
+== Synopsis
+
+[verse]
+*crashpad_handler* ['OPTION…']
+
+== Description
+
+This program is Crashpad’s main exception-handling server. It is responsible for
+catching exceptions, writing crash reports, and uploading them to a crash report
+collection server. Uploads are disabled by default, and can only be enabled by a
+Crashpad client using the Crashpad client library, typically in response to a
+user requesting this behavior.
+
+This server is normally started by its initial client, and it performs a
+handshake with this client via a pipe established by the client that is
+inherited by the server, referenced by the *--handshake-fd* argument. During the
+handshake, the server furnishes the client with a send right that the client may
+use as an exception port. The server retains the corresponding receive right,
+which it monitors for exception messages. When the receive right loses all
+senders, the server exits after allowing any upload in progress to complete.
+
+It is not normally appropriate to invoke this program directly. Usually, it will
+be invoked by a Crashpad client using the Crashpad client library. Arbitrary
+programs may be run with a Crashpad handler by using
+man_link:run_with_crashpad[1] to establish the Crashpad client environment
+before running a program.
+
+== Options
+*--annotation*='KEY=VALUE'::
+Sets a process-level annotation mapping 'KEY' to 'VALUE' in each crash report
+that is written. This option may appear zero, one, or multiple times.
++
+Most annotations should be provided by the Crashpad client as module-level
+annotations instead of process-level annotations. Module-level annotations are
+more flexible in that they can be modified and cleared during the client
+program’s lifetime. Module-level annotations can be set via the Crashpad client
+library. Process-level annotations are useful for annotations that the
+collection server requires be present, that have fixed values, and for cases
+where a program that does not use the Crashpad client library is being
+monitored.
++
+Breakpad-type collection servers only require the +"prod"+ and +"ver"+
+annotations, which should be set to the product name or identifier and product
+version, respectively. It is unusual to specify other annotations as
+process-level annotations via this argument.
+
+*--database*='PATH'::
+Use 'PATH' as the path to the Crashpad crash report database. This option is
+required. Crash reports are written to this database, and if uploads are
+enabled, uploaded from this database to a crash report collection server. If the
+database does not exist, it will be created, provided that the parent directory
+of 'PATH' exists.
+
+*--handshake-fd*='FD'::
+Perform the handshake with the initial client on the file descriptor at 'FD'.
+This option is required.
+
+*--url*='URL'::
+If uploads are enabled, sends crash reports to the Breakpad-type crash report
+collection server at 'URL'. Uploads are disabled by default, and can only be
+enabled for a database by a Crashpad client using the Crashpad client library,
+typically in response to a user requesting this behavior. If this option is not
+specified, this program will behave as if uploads are disabled.
+
+*--help*::
+Display help and exit.
+
+*--version*::
+Output version information and exit.
+
+== Exit Status
+
+*0*::
+Success.
+
+*1*::
+Failure, with a message printed to the standard error stream.
+
+== See Also
+
+man_link:catch_exception_tool[1],
+man_link:crashpad_database_util[1],
+man_link:generate_dump[1],
+man_link:run_with_crashpad[1]
+
+include::../../doc/support/man_footer.ad[]
diff --git a/chromium/third_party/crashpad/crashpad/handler/mac/exception_handler_server.cc b/chromium/third_party/crashpad/crashpad/handler/mac/exception_handler_server.cc
new file mode 100644
index 00000000000..39348ec4953
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/handler/mac/exception_handler_server.cc
@@ -0,0 +1,236 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "handler/mac/exception_handler_server.h"
+
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "util/mach/composite_mach_message_server.h"
+#include "util/mach/mach_extensions.h"
+#include "util/mach/mach_message.h"
+#include "util/mach/mach_message_server.h"
+#include "util/mach/notify_server.h"
+
+namespace crashpad {
+
+namespace {
+
+class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface,
+ public NotifyServer::Interface {
+ public:
+ ExceptionHandlerServerRun(
+ mach_port_t exception_port,
+ UniversalMachExcServer::Interface* exception_interface)
+ : UniversalMachExcServer::Interface(),
+ NotifyServer::Interface(),
+ mach_exc_server_(this),
+ notify_server_(this),
+ composite_mach_message_server_(),
+ exception_interface_(exception_interface),
+ exception_port_(exception_port),
+ notify_port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)),
+ running_(true) {
+ CHECK_NE(notify_port_, kMachPortNull);
+
+ composite_mach_message_server_.AddHandler(&mach_exc_server_);
+ composite_mach_message_server_.AddHandler(&notify_server_);
+ }
+
+ ~ExceptionHandlerServerRun() {
+ }
+
+ void Run() {
+ DCHECK(running_);
+
+ // Request that a no-senders notification for exception_port_ be sent to
+ // notify_port_.
+ mach_port_t previous;
+ kern_return_t kr =
+ mach_port_request_notification(mach_task_self(),
+ exception_port_,
+ MACH_NOTIFY_NO_SENDERS,
+ 0,
+ notify_port_,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE,
+ &previous);
+ MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_request_notification";
+
+ if (previous != MACH_PORT_NULL) {
+ kr = mach_port_deallocate(mach_task_self(), previous);
+ MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_deallocate";
+ }
+
+ // A single CompositeMachMessageServer will dispatch both exception messages
+ // and the no-senders notification. Put both receive rights into a port set.
+ //
+ // A single receive right can’t be used because the notification request
+ // requires a send-once right, which would prevent the no-senders condition
+ // from ever existing. Using distinct receive rights also allows the handler
+ // methods to ensure that the messages they process were sent by a holder of
+ // the proper send right.
+ base::mac::ScopedMachPortSet server_port_set(
+ NewMachPort(MACH_PORT_RIGHT_PORT_SET));
+
+ kr = mach_port_insert_member(
+ mach_task_self(), exception_port_, server_port_set);
+ MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member";
+
+ kr = mach_port_insert_member(
+ mach_task_self(), notify_port_, server_port_set);
+ MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member";
+
+ // Run the server in kOneShot mode so that running_ can be reevaluated after
+ // each message. Receipt of a valid no-senders notification causes it to be
+ // set to false.
+ while (running_) {
+ // This will result in a call to CatchMachException() or
+ // DoMachNotifyNoSenders() as appropriate.
+ mach_msg_return_t mr =
+ MachMessageServer::Run(&composite_mach_message_server_,
+ server_port_set,
+ kMachMessageReceiveAuditTrailer,
+ MachMessageServer::kOneShot,
+ MachMessageServer::kReceiveLargeIgnore,
+ kMachMessageTimeoutWaitIndefinitely);
+ MACH_CHECK(mr == MACH_MSG_SUCCESS, mr) << "MachMessageServer::Run";
+ }
+ }
+
+ // UniversalMachExcServer::Interface:
+
+ kern_return_t CatchMachException(exception_behavior_t behavior,
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_complex_request) override {
+ if (exception_port != exception_port_) {
+ LOG(WARNING) << "exception port mismatch";
+ return MIG_BAD_ID;
+ }
+
+ return exception_interface_->CatchMachException(behavior,
+ exception_port,
+ thread,
+ task,
+ exception,
+ code,
+ code_count,
+ flavor,
+ old_state,
+ old_state_count,
+ new_state,
+ new_state_count,
+ trailer,
+ destroy_complex_request);
+ }
+
+ // NotifyServer::Interface:
+
+ kern_return_t DoMachNotifyPortDeleted(
+ notify_port_t notify,
+ mach_port_name_t name,
+ const mach_msg_trailer_t* trailer) override {
+ return UnimplementedNotifyRoutine(notify);
+ }
+
+ kern_return_t DoMachNotifyPortDestroyed(notify_port_t notify,
+ mach_port_t rights,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_request) override {
+ *destroy_request = true;
+ return UnimplementedNotifyRoutine(notify);
+ }
+
+ kern_return_t DoMachNotifyNoSenders(
+ notify_port_t notify,
+ mach_port_mscount_t mscount,
+ const mach_msg_trailer_t* trailer) override {
+ if (notify != notify_port_) {
+ // The message was received as part of a port set. This check ensures that
+ // only the authorized sender of the no-senders notification is able to
+ // stop the exception server. Otherwise, a malicious client would be able
+ // to craft and send a no-senders notification via its exception port, and
+ // cause the handler to stop processing exceptions and exit.
+ LOG(WARNING) << "notify port mismatch";
+ return MIG_BAD_ID;
+ }
+
+ running_ = false;
+
+ return KERN_SUCCESS;
+ }
+
+ kern_return_t DoMachNotifySendOnce(
+ notify_port_t notify,
+ const mach_msg_trailer_t* trailer) override {
+ return UnimplementedNotifyRoutine(notify);
+ }
+
+ kern_return_t DoMachNotifyDeadName(
+ notify_port_t notify,
+ mach_port_name_t name,
+ const mach_msg_trailer_t* trailer) override {
+ return UnimplementedNotifyRoutine(notify);
+ }
+
+ private:
+ kern_return_t UnimplementedNotifyRoutine(notify_port_t notify) {
+ // Most of the routines in the notify subsystem are not expected to be
+ // called.
+ if (notify != notify_port_) {
+ LOG(WARNING) << "notify port mismatch";
+ return MIG_BAD_ID;
+ }
+
+ NOTREACHED();
+ return KERN_FAILURE;
+ }
+
+ UniversalMachExcServer mach_exc_server_;
+ NotifyServer notify_server_;
+ CompositeMachMessageServer composite_mach_message_server_;
+ UniversalMachExcServer::Interface* exception_interface_; // weak
+ mach_port_t exception_port_; // weak
+ base::mac::ScopedMachReceiveRight notify_port_;
+ bool running_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServerRun);
+};
+
+} // namespace
+
+ExceptionHandlerServer::ExceptionHandlerServer()
+ : receive_port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)) {
+ CHECK_NE(receive_port_, kMachPortNull);
+}
+
+ExceptionHandlerServer::~ExceptionHandlerServer() {
+}
+
+void ExceptionHandlerServer::Run(
+ UniversalMachExcServer::Interface* exception_interface) {
+ ExceptionHandlerServerRun run(receive_port_, exception_interface);
+ run.Run();
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/handler/mac/exception_handler_server.h b/chromium/third_party/crashpad/crashpad/handler/mac/exception_handler_server.h
new file mode 100644
index 00000000000..37c61a0f486
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/handler/mac/exception_handler_server.h
@@ -0,0 +1,70 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_HANDLER_MAC_EXCEPTION_HANDLER_SERVER_H_
+#define CRASHPAD_HANDLER_MAC_EXCEPTION_HANDLER_SERVER_H_
+
+#include "base/basictypes.h"
+
+#include <mach/mach.h>
+
+#include "base/mac/scoped_mach_port.h"
+#include "util/mach/exc_server_variants.h"
+
+namespace crashpad {
+
+//! \brief Runs the main exception-handling server in Crashpad’s handler
+//! process.
+class ExceptionHandlerServer {
+ public:
+ ExceptionHandlerServer();
+ ~ExceptionHandlerServer();
+
+ //! \brief Runs the exception-handling server.
+ //!
+ //! \param[in] exception_interface An object to send exception messages to.
+ //!
+ //! This method monitors receive_port() for exception messages and no-senders
+ //! notifications. It continues running until it has no more clients,
+ //! indicated by the receipt of a no-senders notification. It is important to
+ //! assure that a send right has been transferred to a client (or queued by
+ //! `mach_msg()` to be sent to a client) prior to calling this method, or it
+ //! will detect that it is sender-less and return immediately.
+ //!
+ //! All exception messages will be passed to \a exception_interface.
+ //!
+ //! This method must only be called once on an ExceptionHandlerServer object.
+ //!
+ //! If an unexpected condition that prevents this method from functioning is
+ //! encountered, it will log a message and terminate execution. Receipt of an
+ //! invalid message on receive_port() will cause a message to be logged, but
+ //! this method will continue running normally.
+ void Run(UniversalMachExcServer::Interface* exception_interface);
+
+ //! \brief Returns the receive right that will be monitored for exception
+ //! messages.
+ //!
+ //! The caller does not take ownership of this port. The caller must not use
+ //! this port for any purpose other than to make send rights for clients.
+ mach_port_t receive_port() const { return receive_port_; }
+
+ private:
+ base::mac::ScopedMachReceiveRight receive_port_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServer);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_HANDLER_MAC_EXCEPTION_HANDLER_SERVER_H_
diff --git a/chromium/third_party/crashpad/crashpad/handler/mac/main.cc b/chromium/third_party/crashpad/crashpad/handler/mac/main.cc
new file mode 100644
index 00000000000..7f0629bbed8
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/handler/mac/main.cc
@@ -0,0 +1,188 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <getopt.h>
+#include <libgen.h>
+#include <stdlib.h>
+
+#include <map>
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "client/crash_report_database.h"
+#include "tools/tool_support.h"
+#include "handler/mac/crash_report_exception_handler.h"
+#include "handler/mac/crash_report_upload_thread.h"
+#include "handler/mac/exception_handler_server.h"
+#include "util/mach/child_port_handshake.h"
+#include "util/posix/close_stdio.h"
+#include "util/stdlib/map_insert.h"
+#include "util/stdlib/string_number_conversion.h"
+#include "util/string/split_string.h"
+#include "util/synchronization/semaphore.h"
+
+namespace crashpad {
+namespace {
+
+void Usage(const std::string& me) {
+ fprintf(stderr,
+"Usage: %s [OPTION]...\n"
+"Crashpad's exception handler server.\n"
+"\n"
+" --annotation=KEY=VALUE set a process annotation in each crash report\n"
+" --database=PATH store the crash report database at PATH\n"
+" --handshake-fd=FD establish communication with the client over FD\n"
+" --url=URL send crash reports to this Breakpad server URL,\n"
+" only if uploads are enabled for the database\n"
+" --help display this help and exit\n"
+" --version output version information and exit\n",
+ me.c_str());
+ ToolSupport::UsageTail(me);
+}
+
+int HandlerMain(int argc, char* argv[]) {
+ const std::string me(basename(argv[0]));
+
+ enum OptionFlags {
+ // Long options without short equivalents.
+ kOptionLastChar = 255,
+ kOptionAnnotation,
+ kOptionDatabase,
+ kOptionHandshakeFD,
+ kOptionURL,
+
+ // Standard options.
+ kOptionHelp = -2,
+ kOptionVersion = -3,
+ };
+
+ struct {
+ std::map<std::string, std::string> annotations;
+ std::string url;
+ const char* database;
+ int handshake_fd;
+ } options = {};
+ options.handshake_fd = -1;
+
+ const option long_options[] = {
+ {"annotation", required_argument, nullptr, kOptionAnnotation},
+ {"database", required_argument, nullptr, kOptionDatabase},
+ {"handshake-fd", required_argument, nullptr, kOptionHandshakeFD},
+ {"url", required_argument, nullptr, kOptionURL},
+ {"help", no_argument, nullptr, kOptionHelp},
+ {"version", no_argument, nullptr, kOptionVersion},
+ {nullptr, 0, nullptr, 0},
+ };
+
+ int opt;
+ while ((opt = getopt_long(argc, argv, "", long_options, nullptr)) != -1) {
+ switch (opt) {
+ case kOptionAnnotation: {
+ std::string key;
+ std::string value;
+ if (!SplitString(optarg, '=', &key, &value)) {
+ ToolSupport::UsageHint(me, "--annotation requires KEY=VALUE");
+ return EXIT_FAILURE;
+ }
+ std::string old_value;
+ if (!MapInsertOrReplace(&options.annotations, key, value, &old_value)) {
+ LOG(WARNING) << "duplicate key " << key << ", discarding value "
+ << old_value;
+ }
+ break;
+ }
+ case kOptionDatabase: {
+ options.database = optarg;
+ break;
+ }
+ case kOptionHandshakeFD: {
+ if (!StringToNumber(optarg, &options.handshake_fd) ||
+ options.handshake_fd < 0) {
+ ToolSupport::UsageHint(me,
+ "--handshake-fd requires a file descriptor");
+ return EXIT_FAILURE;
+ }
+ break;
+ }
+ case kOptionURL: {
+ options.url = optarg;
+ break;
+ }
+ case kOptionHelp: {
+ Usage(me);
+ return EXIT_SUCCESS;
+ }
+ case kOptionVersion: {
+ ToolSupport::Version(me);
+ return EXIT_SUCCESS;
+ }
+ default: {
+ ToolSupport::UsageHint(me, nullptr);
+ return EXIT_FAILURE;
+ }
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (options.handshake_fd < 0) {
+ ToolSupport::UsageHint(me, "--handshake-fd is required");
+ return EXIT_FAILURE;
+ }
+
+ if (!options.database) {
+ ToolSupport::UsageHint(me, "--database is required");
+ return EXIT_FAILURE;
+ }
+
+ if (argc) {
+ ToolSupport::UsageHint(me, nullptr);
+ return EXIT_FAILURE;
+ }
+
+ CloseStdinAndStdout();
+
+ ExceptionHandlerServer exception_handler_server;
+
+ ChildPortHandshake::RunClient(options.handshake_fd,
+ exception_handler_server.receive_port(),
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ scoped_ptr<CrashReportDatabase> database(
+ CrashReportDatabase::Initialize(base::FilePath(options.database)));
+ if (!database) {
+ return EXIT_FAILURE;
+ }
+
+ CrashReportUploadThread upload_thread(database.get(), options.url);
+ upload_thread.Start();
+
+ CrashReportExceptionHandler exception_handler(
+ database.get(), &upload_thread, &options.annotations);
+
+ exception_handler_server.Run(&exception_handler);
+
+ upload_thread.Stop();
+
+ return EXIT_SUCCESS;
+}
+
+} // namespace
+} // namespace crashpad
+
+int main(int argc, char* argv[]) {
+ return crashpad::HandlerMain(argc, argv);
+}
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump.gyp b/chromium/third_party/crashpad/crashpad/minidump/minidump.gyp
new file mode 100644
index 00000000000..f7da929c69b
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump.gyp
@@ -0,0 +1,76 @@
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'includes': [
+ '../build/crashpad.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'crashpad_minidump',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../compat/compat.gyp:crashpad_compat',
+ '../snapshot/snapshot.gyp:crashpad_snapshot',
+ '../third_party/mini_chromium/mini_chromium.gyp:base',
+ '../util/util.gyp:crashpad_util',
+ ],
+ 'export_dependent_settings': [
+ '../compat/compat.gyp:crashpad_compat',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'minidump_context.h',
+ 'minidump_context_writer.cc',
+ 'minidump_context_writer.h',
+ 'minidump_crashpad_info_writer.cc',
+ 'minidump_crashpad_info_writer.h',
+ 'minidump_exception_writer.cc',
+ 'minidump_exception_writer.h',
+ 'minidump_extensions.cc',
+ 'minidump_extensions.h',
+ 'minidump_file_writer.cc',
+ 'minidump_file_writer.h',
+ 'minidump_memory_writer.cc',
+ 'minidump_memory_writer.h',
+ 'minidump_misc_info_writer.cc',
+ 'minidump_misc_info_writer.h',
+ 'minidump_module_crashpad_info_writer.cc',
+ 'minidump_module_crashpad_info_writer.h',
+ 'minidump_module_writer.cc',
+ 'minidump_module_writer.h',
+ 'minidump_rva_list_writer.cc',
+ 'minidump_rva_list_writer.h',
+ 'minidump_simple_string_dictionary_writer.cc',
+ 'minidump_simple_string_dictionary_writer.h',
+ 'minidump_stream_writer.cc',
+ 'minidump_stream_writer.h',
+ 'minidump_string_writer.cc',
+ 'minidump_string_writer.h',
+ 'minidump_system_info_writer.cc',
+ 'minidump_system_info_writer.h',
+ 'minidump_thread_id_map.cc',
+ 'minidump_thread_id_map.h',
+ 'minidump_thread_writer.cc',
+ 'minidump_thread_writer.h',
+ 'minidump_writable.cc',
+ 'minidump_writable.h',
+ 'minidump_writer_util.cc',
+ 'minidump_writer_util.h',
+ ],
+ },
+ ],
+}
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_context.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_context.h
new file mode 100644
index 00000000000..4eb3acfa3cd
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_context.h
@@ -0,0 +1,344 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_H_
+#define CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_H_
+
+#include <stdint.h>
+
+#include "base/compiler_specific.h"
+#include "snapshot/cpu_context.h"
+#include "util/numeric/int128.h"
+
+namespace crashpad {
+
+//! \brief Architecture-independent flags for `context_flags` fields in Minidump
+//! context structures.
+//
+// http://zachsaw.blogspot.com/2010/11/wow64-bug-getthreadcontext-may-return.html#c5639760895973344002
+enum MinidumpContextFlags : uint32_t {
+ //! \brief The thread was executing a trap handler in kernel mode
+ //! (`CONTEXT_EXCEPTION_ACTIVE`).
+ //!
+ //! If this bit is set, it indicates that the context is from a thread that
+ //! was executing a trap handler in the kernel. This bit is only valid when
+ //! ::kMinidumpContextExceptionReporting is also set. This bit is only used on
+ //! Windows.
+ kMinidumpContextExceptionActive = 0x08000000,
+
+ //! \brief The thread was executing a system call in kernel mode
+ //! (`CONTEXT_SERVICE_ACTIVE`).
+ //!
+ //! If this bit is set, it indicates that the context is from a thread that
+ //! was executing a system call in the kernel. This bit is only valid when
+ //! ::kMinidumpContextExceptionReporting is also set. This bit is only used on
+ //! Windows.
+ kMinidumpContextServiceActive = 0x10000000,
+
+ //! \brief Kernel-mode state reporting is desired
+ //! (`CONTEXT_EXCEPTION_REQUEST`).
+ //!
+ //! This bit is not used in context structures containing snapshots of thread
+ //! CPU context. It used when calling `GetThreadContext()` on Windows to
+ //! specify that kernel-mode state reporting
+ //! (::kMinidumpContextExceptionReporting) is desired in the returned context
+ //! structure.
+ kMinidumpContextExceptionRequest = 0x40000000,
+
+ //! \brief Kernel-mode state reporting is provided
+ //! (`CONTEXT_EXCEPTION_REPORTING`).
+ //!
+ //! If this bit is set, it indicates that the bits indicating how the thread
+ //! had entered kernel mode (::kMinidumpContextExceptionActive and
+ //! and ::kMinidumpContextServiceActive) are valid. This bit is only used on
+ //! Windows.
+ kMinidumpContextExceptionReporting = 0x80000000,
+};
+
+//! \brief 32-bit x86-specifc flags for MinidumpContextX86::context_flags.
+enum MinidumpContextX86Flags : uint32_t {
+ //! \brief Identifies the context structure as 32-bit x86. This is the same as
+ //! `CONTEXT_i386` and `CONTEXT_i486` on Windows for this architecture.
+ kMinidumpContextX86 = 0x00010000,
+
+ //! \brief Indicates the validity of control registers (`CONTEXT_CONTROL`).
+ //!
+ //! The `ebp`, `eip`, `cs`, `eflags`, `esp`, and `ss` fields are valid.
+ kMinidumpContextX86Control = kMinidumpContextX86 | 0x00000001,
+
+ //! \brief Indicates the validity of non-control integer registers
+ //! (`CONTEXT_INTEGER`).
+ //!
+ //! The `edi`, `esi`, `ebx`, `edx`, `ecx, and `eax` fields are valid.
+ kMinidumpContextX86Integer = kMinidumpContextX86 | 0x00000002,
+
+ //! \brief Indicates the validity of non-control segment registers
+ //! (`CONTEXT_SEGMENTS`).
+ //!
+ //! The `gs`, `fs`, `es`, and `ds` fields are valid.
+ kMinidumpContextX86Segment = kMinidumpContextX86 | 0x00000004,
+
+ //! \brief Indicates the validity of floating-point state
+ //! (`CONTEXT_FLOATING_POINT`).
+ //!
+ //! The `float_save` field is valid.
+ kMinidumpContextX86FloatingPoint = kMinidumpContextX86 | 0x00000008,
+
+ //! \brief Indicates the validity of debug registers
+ //! (`CONTEXT_DEBUG_REGISTERS`).
+ //!
+ //! The `dr0` through `dr3`, `dr6`, and `dr7` fields are valid.
+ kMinidumpContextX86Debug = kMinidumpContextX86 | 0x00000010,
+
+ //! \brief Indicates the validity of extended registers in `fxsave` format
+ //! (`CONTEXT_EXTENDED_REGISTERS`).
+ //!
+ //! The `extended_registers` field is valid and contains `fxsave` data.
+ kMinidumpContextX86Extended = kMinidumpContextX86 | 0x00000020,
+
+ //! \brief Indicates the validity of `xsave` data (`CONTEXT_XSTATE`).
+ //!
+ //! The context contains `xsave` data. This is used with an extended context
+ //! structure not currently defined here.
+ kMinidumpContextX86Xstate = kMinidumpContextX86 | 0x00000040,
+
+ //! \brief Indicates the validity of control, integer, and segment registers.
+ //! (`CONTEXT_FULL`).
+ kMinidumpContextX86Full = kMinidumpContextX86Control |
+ kMinidumpContextX86Integer |
+ kMinidumpContextX86Segment,
+
+ //! \brief Indicates the validity of all registers except `xsave` data.
+ //! (`CONTEXT_ALL`).
+ kMinidumpContextX86All = kMinidumpContextX86Full |
+ kMinidumpContextX86FloatingPoint |
+ kMinidumpContextX86Debug |
+ kMinidumpContextX86Extended,
+};
+
+//! \brief A 32-bit x86 CPU context (register state) carried in a minidump file.
+//!
+//! This is analogous to the `CONTEXT` structure on Windows when targeting
+//! 32-bit x86. This structure is used instead of `CONTEXT` to make it available
+//! when targeting other architectures.
+//!
+//! \note This structure doesn’t carry `dr4` or `dr5`, which are obsolete and
+//! normally alias `dr6` and `dr7`, respectively. See Intel Software
+//! Developer’s Manual, Volume 3B: System Programming, Part 2 (253669-052),
+//! 17.2.2 “Debug Registers DR4 and DR5”.
+struct MinidumpContextX86 {
+ //! \brief A bitfield composed of values of #MinidumpContextFlags and
+ //! #MinidumpContextX86Flags.
+ //!
+ //! This field identifies the context structure as a 32-bit x86 CPU context,
+ //! and indicates which other fields in the structure are valid.
+ uint32_t context_flags;
+
+ uint32_t dr0;
+ uint32_t dr1;
+ uint32_t dr2;
+ uint32_t dr3;
+ uint32_t dr6;
+ uint32_t dr7;
+
+ struct {
+ uint32_t control_word;
+ uint32_t status_word;
+ uint32_t tag_word;
+ uint32_t error_offset;
+ uint32_t error_selector;
+ uint32_t data_offset;
+ uint32_t data_selector;
+ uint8_t register_area[80];
+ uint32_t spare_0;
+ } float_save;
+
+ uint32_t gs;
+ uint32_t fs;
+ uint32_t es;
+ uint32_t ds;
+
+ uint32_t edi;
+ uint32_t esi;
+ uint32_t ebx;
+ uint32_t edx;
+ uint32_t ecx;
+ uint32_t eax;
+
+ uint32_t ebp;
+ uint32_t eip;
+ uint32_t cs;
+ uint32_t eflags;
+ uint32_t esp;
+ uint32_t ss;
+
+ // CPUContextX86::Fxsave has identical layout to what the x86 CONTEXT
+ // structure places here.
+ CPUContextX86::Fxsave fxsave;
+};
+
+//! \brief x86_64-specific flags for MinidumpContextAMD64::context_flags.
+enum MinidumpContextAMD64Flags : uint32_t {
+ //! \brief Identifies the context structure as x86_64. This is the same as
+ //! `CONTEXT_AMD64` on Windows for this architecture.
+ kMinidumpContextAMD64 = 0x00100000,
+
+ //! \brief Indicates the validity of control registers (`CONTEXT_CONTROL`).
+ //!
+ //! The `cs`, `ss`, `eflags`, `rsp`, and `rip` fields are valid.
+ kMinidumpContextAMD64Control = kMinidumpContextAMD64 | 0x00000001,
+
+ //! \brief Indicates the validity of non-control integer registers
+ //! (`CONTEXT_INTEGER`).
+ //!
+ //! The `rax`, `rcx`, `rdx`, `rbx`, `rbp`, `rsi`, `rdi`, and `r8` through
+ //! `r15` fields are valid.
+ kMinidumpContextAMD64Integer = kMinidumpContextAMD64 | 0x00000002,
+
+ //! \brief Indicates the validity of non-control segment registers
+ //! (`CONTEXT_SEGMENTS`).
+ //!
+ //! The `ds`, `es`, `fs`, and `gs` fields are valid.
+ kMinidumpContextAMD64Segment = kMinidumpContextAMD64 | 0x00000004,
+
+ //! \brief Indicates the validity of floating-point state
+ //! (`CONTEXT_FLOATING_POINT`).
+ //!
+ //! The `xmm0` through `xmm15` fields are valid.
+ kMinidumpContextAMD64FloatingPoint = kMinidumpContextAMD64 | 0x00000008,
+
+ //! \brief Indicates the validity of debug registers
+ //! (`CONTEXT_DEBUG_REGISTERS`).
+ //!
+ //! The `dr0` through `dr3`, `dr6`, and `dr7` fields are valid.
+ kMinidumpContextAMD64Debug = kMinidumpContextAMD64 | 0x00000010,
+
+ //! \brief Indicates the validity of `xsave` data (`CONTEXT_XSTATE`).
+ //!
+ //! The context contains `xsave` data. This is used with an extended context
+ //! structure not currently defined here.
+ kMinidumpContextAMD64Xstate = kMinidumpContextAMD64 | 0x00000040,
+
+ //! \brief Indicates the validity of control, integer, and floating-point
+ //! registers (`CONTEXT_FULL`).
+ kMinidumpContextAMD64Full = kMinidumpContextAMD64Control |
+ kMinidumpContextAMD64Integer |
+ kMinidumpContextAMD64FloatingPoint,
+
+ //! \brief Indicates the validity of all registers except `xsave` data
+ //! (`CONTEXT_ALL`).
+ kMinidumpContextAMD64All = kMinidumpContextAMD64Full |
+ kMinidumpContextAMD64Segment |
+ kMinidumpContextAMD64Debug,
+};
+
+//! \brief An x86_64 (AMD64) CPU context (register state) carried in a minidump
+//! file.
+//!
+//! This is analogous to the `CONTEXT` structure on Windows when targeting
+//! x86_64. This structure is used instead of `CONTEXT` to make it available
+//! when targeting other architectures.
+//!
+//! \note This structure doesn’t carry `dr4` or `dr5`, which are obsolete and
+//! normally alias `dr6` and `dr7`, respectively. See Intel Software
+//! Developer’s Manual, Volume 3B: System Programming, Part 2 (253669-052),
+//! 17.2.2 “Debug Registers DR4 and DR5”.
+struct ALIGNAS(16) MinidumpContextAMD64 {
+ //! \brief Register parameter home address.
+ //!
+ //! On Windows, this field may contain the “home” address (on-stack, in the
+ //! shadow area) of a parameter passed by register. This field is present for
+ //! convenience but is not necessarily populated, even if a corresponding
+ //! parameter was passed by register.
+ //!
+ //! \{
+ uint64_t p1_home;
+ uint64_t p2_home;
+ uint64_t p3_home;
+ uint64_t p4_home;
+ uint64_t p5_home;
+ uint64_t p6_home;
+ //! \}
+
+ //! \brief A bitfield composed of values of #MinidumpContextFlags and
+ //! #MinidumpContextAMD64Flags.
+ //!
+ //! This field identifies the context structure as an x86_64 CPU context, and
+ //! indicates which other fields in the structure are valid.
+ uint32_t context_flags;
+
+ uint32_t mx_csr;
+
+ uint16_t cs;
+ uint16_t ds;
+ uint16_t es;
+ uint16_t fs;
+ uint16_t gs;
+ uint16_t ss;
+
+ uint32_t eflags;
+
+ uint64_t dr0;
+ uint64_t dr1;
+ uint64_t dr2;
+ uint64_t dr3;
+ uint64_t dr6;
+ uint64_t dr7;
+
+ uint64_t rax;
+ uint64_t rcx;
+ uint64_t rdx;
+ uint64_t rbx;
+ uint64_t rsp;
+ uint64_t rbp;
+ uint64_t rsi;
+ uint64_t rdi;
+ uint64_t r8;
+ uint64_t r9;
+ uint64_t r10;
+ uint64_t r11;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+
+ uint64_t rip;
+
+ // CPUContextX86_64::Fxsave has identical layout to what the x86_64 CONTEXT
+ // structure places here.
+ CPUContextX86_64::Fxsave fxsave;
+
+ uint128_struct vector_register[26];
+ uint64_t vector_control;
+
+ //! \brief Model-specific debug extension register.
+ //!
+ //! See Intel Software Developer’s Manual, Volume 3B: System Programming, Part
+ //! 2 (253669-051), 17.4 “Last Branch, Interrupt, and Exception Recording
+ //! Overview”, and AMD Architecture Programmer’s Manual, Volume 2:
+ //! System Programming (24593-3.24), 13.1.6 “Control-Transfer Breakpoint
+ //! Features”.
+ //!
+ //! \{
+ uint64_t debug_control;
+ uint64_t last_branch_to_rip;
+ uint64_t last_branch_from_rip;
+ uint64_t last_exception_to_rip;
+ uint64_t last_exception_from_rip;
+ //! \}
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_H_
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.cc
new file mode 100644
index 00000000000..fef1af31d28
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.cc
@@ -0,0 +1,215 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_context_writer.h"
+
+#include <string.h>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "snapshot/cpu_context.h"
+#include "util/file/file_writer.h"
+
+namespace crashpad {
+
+MinidumpContextWriter::~MinidumpContextWriter() {
+}
+
+// static
+scoped_ptr<MinidumpContextWriter> MinidumpContextWriter::CreateFromSnapshot(
+ const CPUContext* context_snapshot) {
+ scoped_ptr<MinidumpContextWriter> context;
+
+ switch (context_snapshot->architecture) {
+ case kCPUArchitectureX86: {
+ MinidumpContextX86Writer* context_x86 = new MinidumpContextX86Writer();
+ context.reset(context_x86);
+ context_x86->InitializeFromSnapshot(context_snapshot->x86);
+ break;
+ }
+
+ case kCPUArchitectureX86_64: {
+ MSVC_PUSH_DISABLE_WARNING(4316); // Object on heap may not be aligned.
+ MinidumpContextAMD64Writer* context_amd64 =
+ new MinidumpContextAMD64Writer();
+ MSVC_POP_WARNING(); // C4316
+ context.reset(context_amd64);
+ context_amd64->InitializeFromSnapshot(context_snapshot->x86_64);
+ break;
+ }
+
+ default: {
+ LOG(ERROR) << "unknown context architecture "
+ << context_snapshot->architecture;
+ break;
+ }
+ }
+
+ return context;
+}
+
+size_t MinidumpContextWriter::SizeOfObject() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ return ContextSize();
+}
+
+MinidumpContextX86Writer::MinidumpContextX86Writer()
+ : MinidumpContextWriter(), context_() {
+ context_.context_flags = kMinidumpContextX86;
+}
+
+MinidumpContextX86Writer::~MinidumpContextX86Writer() {
+}
+
+
+void MinidumpContextX86Writer::InitializeFromSnapshot(
+ const CPUContextX86* context_snapshot) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK_EQ(context_.context_flags, kMinidumpContextX86);
+
+ context_.context_flags = kMinidumpContextX86All;
+
+ context_.dr0 = context_snapshot->dr0;
+ context_.dr1 = context_snapshot->dr1;
+ context_.dr2 = context_snapshot->dr2;
+ context_.dr3 = context_snapshot->dr3;
+ context_.dr6 = context_snapshot->dr6;
+ context_.dr7 = context_snapshot->dr7;
+
+ // The contents of context_.float_save effectively alias everything in
+ // context_.fxsave that’s related to x87 FPU state. context_.float_save
+ // doesn’t carry state specific to SSE (or later), such as mxcsr and the xmm
+ // registers.
+ context_.float_save.control_word = context_snapshot->fxsave.fcw;
+ context_.float_save.status_word = context_snapshot->fxsave.fsw;
+ context_.float_save.tag_word =
+ CPUContextX86::FxsaveToFsaveTagWord(context_snapshot->fxsave.fsw,
+ context_snapshot->fxsave.ftw,
+ context_snapshot->fxsave.st_mm);
+ context_.float_save.error_offset = context_snapshot->fxsave.fpu_ip;
+ context_.float_save.error_selector = context_snapshot->fxsave.fpu_cs;
+ context_.float_save.data_offset = context_snapshot->fxsave.fpu_dp;
+ context_.float_save.data_selector = context_snapshot->fxsave.fpu_ds;
+
+ for (size_t index = 0, offset = 0;
+ index < arraysize(context_snapshot->fxsave.st_mm);
+ offset += sizeof(context_snapshot->fxsave.st_mm[index].st), ++index) {
+ memcpy(&context_.float_save.register_area[offset],
+ &context_snapshot->fxsave.st_mm[index].st,
+ sizeof(context_snapshot->fxsave.st_mm[index].st));
+ }
+
+ context_.gs = context_snapshot->gs;
+ context_.fs = context_snapshot->fs;
+ context_.es = context_snapshot->es;
+ context_.ds = context_snapshot->ds;
+ context_.edi = context_snapshot->edi;
+ context_.esi = context_snapshot->esi;
+ context_.ebx = context_snapshot->ebx;
+ context_.edx = context_snapshot->edx;
+ context_.ecx = context_snapshot->ecx;
+ context_.eax = context_snapshot->eax;
+ context_.ebp = context_snapshot->ebp;
+ context_.eip = context_snapshot->eip;
+ context_.cs = context_snapshot->cs;
+ context_.eflags = context_snapshot->eflags;
+ context_.esp = context_snapshot->esp;
+ context_.ss = context_snapshot->ss;
+
+ // This is effectively a memcpy() of a big structure.
+ context_.fxsave = context_snapshot->fxsave;
+}
+
+bool MinidumpContextX86Writer::WriteObject(FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateWritable);
+
+ return file_writer->Write(&context_, sizeof(context_));
+}
+
+size_t MinidumpContextX86Writer::ContextSize() const {
+ DCHECK_GE(state(), kStateFrozen);
+
+ return sizeof(context_);
+}
+
+MinidumpContextAMD64Writer::MinidumpContextAMD64Writer()
+ : MinidumpContextWriter(), context_() {
+ context_.context_flags = kMinidumpContextAMD64;
+}
+
+MinidumpContextAMD64Writer::~MinidumpContextAMD64Writer() {
+}
+
+void MinidumpContextAMD64Writer::InitializeFromSnapshot(
+ const CPUContextX86_64* context_snapshot) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK_EQ(context_.context_flags, kMinidumpContextAMD64);
+
+ context_.context_flags = kMinidumpContextAMD64All;
+
+ context_.mx_csr = context_snapshot->fxsave.mxcsr;
+ context_.cs = context_snapshot->cs;
+ context_.fs = context_snapshot->fs;
+ context_.gs = context_snapshot->gs;
+ // The top 32 bits of rflags are reserved/unused.
+ context_.eflags = static_cast<uint32_t>(context_snapshot->rflags);
+ context_.dr0 = context_snapshot->dr0;
+ context_.dr1 = context_snapshot->dr1;
+ context_.dr2 = context_snapshot->dr2;
+ context_.dr3 = context_snapshot->dr3;
+ context_.dr6 = context_snapshot->dr6;
+ context_.dr7 = context_snapshot->dr7;
+ context_.rax = context_snapshot->rax;
+ context_.rcx = context_snapshot->rcx;
+ context_.rdx = context_snapshot->rdx;
+ context_.rbx = context_snapshot->rbx;
+ context_.rsp = context_snapshot->rsp;
+ context_.rbp = context_snapshot->rbp;
+ context_.rsi = context_snapshot->rsi;
+ context_.rdi = context_snapshot->rdi;
+ context_.r8 = context_snapshot->r8;
+ context_.r9 = context_snapshot->r9;
+ context_.r10 = context_snapshot->r10;
+ context_.r11 = context_snapshot->r11;
+ context_.r12 = context_snapshot->r12;
+ context_.r13 = context_snapshot->r13;
+ context_.r14 = context_snapshot->r14;
+ context_.r15 = context_snapshot->r15;
+ context_.rip = context_snapshot->rip;
+
+ // This is effectively a memcpy() of a big structure.
+ context_.fxsave = context_snapshot->fxsave;
+}
+
+size_t MinidumpContextAMD64Writer::Alignment() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ // Match the alignment of MinidumpContextAMD64.
+ return 16;
+}
+
+bool MinidumpContextAMD64Writer::WriteObject(FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateWritable);
+
+ return file_writer->Write(&context_, sizeof(context_));
+}
+
+size_t MinidumpContextAMD64Writer::ContextSize() const {
+ DCHECK_GE(state(), kStateFrozen);
+
+ return sizeof(context_);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.h
new file mode 100644
index 00000000000..e48650e32c8
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.h
@@ -0,0 +1,149 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_
+#define CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_
+
+#include <sys/types.h>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "minidump/minidump_context.h"
+#include "minidump/minidump_writable.h"
+
+namespace crashpad {
+
+struct CPUContext;
+struct CPUContextX86;
+struct CPUContextX86_64;
+
+//! \brief The base class for writers of CPU context structures in minidump
+//! files.
+class MinidumpContextWriter : public internal::MinidumpWritable {
+ public:
+ ~MinidumpContextWriter() override;
+
+ //! \brief Creates a MinidumpContextWriter based on \a context_snapshot.
+ //!
+ //! \param[in] context_snapshot The context snapshot to use as source data.
+ //!
+ //! \return A MinidumpContextWriter subclass, such as MinidumpContextWriterX86
+ //! or MinidumpContextWriterAMD64, appropriate to the CPU type of \a
+ //! context_snapshot. The returned object is initialized using the source
+ //! data in \a context_snapshot. If \a context_snapshot is an unknown CPU
+ //! type’s context, logs a message and returns `nullptr`.
+ static scoped_ptr<MinidumpContextWriter> CreateFromSnapshot(
+ const CPUContext* context_snapshot);
+
+ protected:
+ MinidumpContextWriter() : MinidumpWritable() {}
+
+ //! \brief Returns the size of the context structure that this object will
+ //! write.
+ //!
+ //! \note This method will only be called in #kStateFrozen or a subsequent
+ //! state.
+ virtual size_t ContextSize() const = 0;
+
+ // MinidumpWritable:
+ size_t SizeOfObject() final;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MinidumpContextWriter);
+};
+
+//! \brief The writer for a MinidumpContextX86 structure in a minidump file.
+class MinidumpContextX86Writer final : public MinidumpContextWriter {
+ public:
+ MinidumpContextX86Writer();
+ ~MinidumpContextX86Writer() override;
+
+ //! \brief Initializes the MinidumpContextX86 based on \a context_snapshot.
+ //!
+ //! \param[in] context_snapshot The context snapshot to use as source data.
+ //!
+ //! \note Valid in #kStateMutable. No mutation of context() may be done before
+ //! calling this method, and it is not normally necessary to alter
+ //! context() after calling this method.
+ void InitializeFromSnapshot(const CPUContextX86* context_snapshot);
+
+ //! \brief Returns a pointer to the context structure that this object will
+ //! write.
+ //!
+ //! \attention This returns a non-`const` pointer to this object’s private
+ //! data so that a caller can populate the context structure directly.
+ //! This is done because providing setter interfaces to each field in the
+ //! context structure would be unwieldy and cumbersome. Care must be taken
+ //! to populate the context structure correctly. The context structure
+ //! must only be modified while this object is in the #kStateMutable
+ //! state.
+ MinidumpContextX86* context() { return &context_; }
+
+ protected:
+ // MinidumpWritable:
+ bool WriteObject(FileWriterInterface* file_writer) override;
+
+ // MinidumpContextWriter:
+ size_t ContextSize() const override;
+
+ private:
+ MinidumpContextX86 context_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpContextX86Writer);
+};
+
+//! \brief The writer for a MinidumpContextAMD64 structure in a minidump file.
+class MinidumpContextAMD64Writer final : public MinidumpContextWriter {
+ public:
+ MinidumpContextAMD64Writer();
+ ~MinidumpContextAMD64Writer() override;
+
+ //! \brief Initializes the MinidumpContextAMD64 based on \a context_snapshot.
+ //!
+ //! \param[in] context_snapshot The context snapshot to use as source data.
+ //!
+ //! \note Valid in #kStateMutable. No mutation of context() may be done before
+ //! calling this method, and it is not normally necessary to alter
+ //! context() after calling this method.
+ void InitializeFromSnapshot(const CPUContextX86_64* context_snapshot);
+
+ //! \brief Returns a pointer to the context structure that this object will
+ //! write.
+ //!
+ //! \attention This returns a non-`const` pointer to this object’s private
+ //! data so that a caller can populate the context structure directly.
+ //! This is done because providing setter interfaces to each field in the
+ //! context structure would be unwieldy and cumbersome. Care must be taken
+ //! to populate the context structure correctly. The context structure
+ //! must only be modified while this object is in the #kStateMutable
+ //! state.
+ MinidumpContextAMD64* context() { return &context_; }
+
+ protected:
+ // MinidumpWritable:
+ size_t Alignment() override;
+ bool WriteObject(FileWriterInterface* file_writer) override;
+
+ // MinidumpContextWriter:
+ size_t ContextSize() const override;
+
+ private:
+ MinidumpContextAMD64 context_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpContextAMD64Writer);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer_test.cc
new file mode 100644
index 00000000000..9c66996bcb7
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer_test.cc
@@ -0,0 +1,157 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_context_writer.h"
+
+#include <stdint.h>
+
+#include "gtest/gtest.h"
+#include "minidump/minidump_context.h"
+#include "minidump/test/minidump_context_test_util.h"
+#include "minidump/test/minidump_writable_test_util.h"
+#include "snapshot/cpu_context.h"
+#include "snapshot/test/test_cpu_context.h"
+#include "util/file/string_file.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(MinidumpContextWriter, MinidumpContextX86Writer) {
+ StringFile string_file;
+
+ {
+ // Make sure that a context writer that’s untouched writes a zeroed-out
+ // context.
+ SCOPED_TRACE("zero");
+
+ MinidumpContextX86Writer context_writer;
+
+ EXPECT_TRUE(context_writer.WriteEverything(&string_file));
+ ASSERT_EQ(sizeof(MinidumpContextX86), string_file.string().size());
+
+ const MinidumpContextX86* observed =
+ MinidumpWritableAtRVA<MinidumpContextX86>(string_file.string(), 0);
+ ASSERT_TRUE(observed);
+
+ ExpectMinidumpContextX86(0, observed, false);
+ }
+
+ {
+ SCOPED_TRACE("nonzero");
+
+ string_file.Reset();
+ const uint32_t kSeed = 0x8086;
+
+ MinidumpContextX86Writer context_writer;
+ InitializeMinidumpContextX86(context_writer.context(), kSeed);
+
+ EXPECT_TRUE(context_writer.WriteEverything(&string_file));
+ ASSERT_EQ(sizeof(MinidumpContextX86), string_file.string().size());
+
+ const MinidumpContextX86* observed =
+ MinidumpWritableAtRVA<MinidumpContextX86>(string_file.string(), 0);
+ ASSERT_TRUE(observed);
+
+ ExpectMinidumpContextX86(kSeed, observed, false);
+ }
+}
+
+TEST(MinidumpContextWriter, MinidumpContextAMD64Writer) {
+ StringFile string_file;
+
+ {
+ // Make sure that a context writer that’s untouched writes a zeroed-out
+ // context.
+ SCOPED_TRACE("zero");
+
+ MinidumpContextAMD64Writer context_writer;
+
+ EXPECT_TRUE(context_writer.WriteEverything(&string_file));
+ ASSERT_EQ(sizeof(MinidumpContextAMD64), string_file.string().size());
+
+ const MinidumpContextAMD64* observed =
+ MinidumpWritableAtRVA<MinidumpContextAMD64>(string_file.string(), 0);
+ ASSERT_TRUE(observed);
+
+ ExpectMinidumpContextAMD64(0, observed, false);
+ }
+
+ {
+ SCOPED_TRACE("nonzero");
+
+ string_file.Reset();
+ const uint32_t kSeed = 0x808664;
+
+ MinidumpContextAMD64Writer context_writer;
+ InitializeMinidumpContextAMD64(context_writer.context(), kSeed);
+
+ EXPECT_TRUE(context_writer.WriteEverything(&string_file));
+ ASSERT_EQ(sizeof(MinidumpContextAMD64), string_file.string().size());
+
+ const MinidumpContextAMD64* observed =
+ MinidumpWritableAtRVA<MinidumpContextAMD64>(string_file.string(), 0);
+ ASSERT_TRUE(observed);
+
+ ExpectMinidumpContextAMD64(kSeed, observed, false);
+ }
+}
+
+TEST(MinidumpContextWriter, CreateFromSnapshot_X86) {
+ const uint32_t kSeed = 32;
+
+ CPUContextX86 context_snapshot_x86;
+ CPUContext context_snapshot;
+ context_snapshot.x86 = &context_snapshot_x86;
+ InitializeCPUContextX86(&context_snapshot, kSeed);
+
+ scoped_ptr<MinidumpContextWriter> context_writer =
+ MinidumpContextWriter::CreateFromSnapshot(&context_snapshot);
+ ASSERT_TRUE(context_writer);
+
+ StringFile string_file;
+ ASSERT_TRUE(context_writer->WriteEverything(&string_file));
+
+ const MinidumpContextX86* observed =
+ MinidumpWritableAtRVA<MinidumpContextX86>(string_file.string(), 0);
+ ASSERT_TRUE(observed);
+
+ ExpectMinidumpContextX86(kSeed, observed, true);
+}
+
+TEST(MinidumpContextWriter, CreateFromSnapshot_AMD64) {
+ const uint32_t kSeed = 64;
+
+ CPUContextX86_64 context_snapshot_x86_64;
+ CPUContext context_snapshot;
+ context_snapshot.x86_64 = &context_snapshot_x86_64;
+ InitializeCPUContextX86_64(&context_snapshot, kSeed);
+
+ scoped_ptr<MinidumpContextWriter> context_writer =
+ MinidumpContextWriter::CreateFromSnapshot(&context_snapshot);
+ ASSERT_TRUE(context_writer);
+
+ StringFile string_file;
+ ASSERT_TRUE(context_writer->WriteEverything(&string_file));
+
+ const MinidumpContextAMD64* observed =
+ MinidumpWritableAtRVA<MinidumpContextAMD64>(string_file.string(), 0);
+ ASSERT_TRUE(observed);
+
+ ExpectMinidumpContextAMD64(kSeed, observed, true);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_crashpad_info_writer.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_crashpad_info_writer.cc
new file mode 100644
index 00000000000..9314224b7cd
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_crashpad_info_writer.cc
@@ -0,0 +1,147 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_crashpad_info_writer.h"
+
+#include "base/logging.h"
+#include "minidump/minidump_module_crashpad_info_writer.h"
+#include "minidump/minidump_simple_string_dictionary_writer.h"
+#include "snapshot/process_snapshot.h"
+#include "util/file/file_writer.h"
+
+namespace crashpad {
+
+MinidumpCrashpadInfoWriter::MinidumpCrashpadInfoWriter()
+ : MinidumpStreamWriter(),
+ crashpad_info_(),
+ simple_annotations_(nullptr),
+ module_list_(nullptr) {
+ crashpad_info_.version = MinidumpCrashpadInfo::kVersion;
+}
+
+MinidumpCrashpadInfoWriter::~MinidumpCrashpadInfoWriter() {
+}
+
+void MinidumpCrashpadInfoWriter::InitializeFromSnapshot(
+ const ProcessSnapshot* process_snapshot) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK(!module_list_);
+
+ UUID report_id;
+ process_snapshot->ReportID(&report_id);
+ SetReportID(report_id);
+
+ UUID client_id;
+ process_snapshot->ClientID(&client_id);
+ SetClientID(client_id);
+
+ auto simple_annotations =
+ make_scoped_ptr(new MinidumpSimpleStringDictionaryWriter());
+ simple_annotations->InitializeFromMap(
+ process_snapshot->AnnotationsSimpleMap());
+ if (simple_annotations->IsUseful()) {
+ SetSimpleAnnotations(simple_annotations.Pass());
+ }
+
+ auto modules = make_scoped_ptr(new MinidumpModuleCrashpadInfoListWriter());
+ modules->InitializeFromSnapshot(process_snapshot->Modules());
+
+ if (modules->IsUseful()) {
+ SetModuleList(modules.Pass());
+ }
+}
+
+void MinidumpCrashpadInfoWriter::SetReportID(const UUID& report_id) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ crashpad_info_.report_id = report_id;
+}
+
+void MinidumpCrashpadInfoWriter::SetClientID(const UUID& client_id) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ crashpad_info_.client_id = client_id;
+}
+
+void MinidumpCrashpadInfoWriter::SetSimpleAnnotations(
+ scoped_ptr<MinidumpSimpleStringDictionaryWriter> simple_annotations) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ simple_annotations_ = simple_annotations.Pass();
+}
+
+void MinidumpCrashpadInfoWriter::SetModuleList(
+ scoped_ptr<MinidumpModuleCrashpadInfoListWriter> module_list) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ module_list_ = module_list.Pass();
+}
+
+bool MinidumpCrashpadInfoWriter::Freeze() {
+ DCHECK_EQ(state(), kStateMutable);
+
+ if (!MinidumpStreamWriter::Freeze()) {
+ return false;
+ }
+
+ if (simple_annotations_) {
+ simple_annotations_->RegisterLocationDescriptor(
+ &crashpad_info_.simple_annotations);
+ }
+ if (module_list_) {
+ module_list_->RegisterLocationDescriptor(&crashpad_info_.module_list);
+ }
+
+ return true;
+}
+
+size_t MinidumpCrashpadInfoWriter::SizeOfObject() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ return sizeof(crashpad_info_);
+}
+
+std::vector<internal::MinidumpWritable*>
+MinidumpCrashpadInfoWriter::Children() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ std::vector<MinidumpWritable*> children;
+ if (simple_annotations_) {
+ children.push_back(simple_annotations_.get());
+ }
+ if (module_list_) {
+ children.push_back(module_list_.get());
+ }
+
+ return children;
+}
+
+bool MinidumpCrashpadInfoWriter::WriteObject(FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateWritable);
+
+ return file_writer->Write(&crashpad_info_, sizeof(crashpad_info_));
+}
+
+MinidumpStreamType MinidumpCrashpadInfoWriter::StreamType() const {
+ return kMinidumpStreamTypeCrashpadInfo;
+}
+
+bool MinidumpCrashpadInfoWriter::IsUseful() const {
+ return crashpad_info_.report_id != UUID() ||
+ crashpad_info_.client_id != UUID() ||
+ simple_annotations_ ||
+ module_list_;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_crashpad_info_writer.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_crashpad_info_writer.h
new file mode 100644
index 00000000000..99945dad979
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_crashpad_info_writer.h
@@ -0,0 +1,112 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_MINIDUMP_MINIDUMP_CRASHPAD_INFO_WRITER_H_
+#define CRASHPAD_MINIDUMP_MINIDUMP_CRASHPAD_INFO_WRITER_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "minidump/minidump_extensions.h"
+#include "minidump/minidump_stream_writer.h"
+#include "util/misc/uuid.h"
+
+namespace crashpad {
+
+class MinidumpModuleCrashpadInfoListWriter;
+class MinidumpSimpleStringDictionaryWriter;
+class ProcessSnapshot;
+
+//! \brief The writer for a MinidumpCrashpadInfo stream in a minidump file.
+class MinidumpCrashpadInfoWriter final : public internal::MinidumpStreamWriter {
+ public:
+ MinidumpCrashpadInfoWriter();
+ ~MinidumpCrashpadInfoWriter() override;
+
+ //! \brief Initializes MinidumpCrashpadInfo based on \a process_snapshot.
+ //!
+ //! This method may add additional structures to the minidump file as children
+ //! of the MinidumpCrashpadInfo stream. To do so, it may obtain other
+ //! snapshot information from \a process_snapshot, such as a list of
+ //! ModuleSnapshot objects used to initialize
+ //! MinidumpCrashpadInfo::module_list. Only data that is considered useful
+ //! will be included. For module information, usefulness is determined by
+ //! MinidumpModuleCrashpadInfoListWriter::IsUseful().
+ //!
+ //! \param[in] process_snapshot The process snapshot to use as source data.
+ //!
+ //! \note Valid in #kStateMutable. No mutator methods may be called before
+ //! this method, and it is not normally necessary to call any mutator
+ //! methods after this method.
+ void InitializeFromSnapshot(const ProcessSnapshot* process_snapshot);
+
+ //! \brief Sets MinidumpCrashpadInfo::report_id.
+ void SetReportID(const UUID& report_id);
+
+ //! \brief Sets MinidumpCrashpadInfo::client_id.
+ void SetClientID(const UUID& client_id);
+
+ //! \brief Arranges for MinidumpCrashpadInfo::simple_annotations to point to
+ //! the MinidumpSimpleStringDictionaryWriter object to be written by \a
+ //! simple_annotations.
+ //!
+ //! This object takes ownership of \a simple_annotations and becomes its
+ //! parent in the overall tree of internal::MinidumpWritable objects.
+ //!
+ //! \note Valid in #kStateMutable.
+ void SetSimpleAnnotations(
+ scoped_ptr<MinidumpSimpleStringDictionaryWriter> simple_annotations);
+
+ //! \brief Arranges for MinidumpCrashpadInfo::module_list to point to the
+ //! MinidumpModuleCrashpadInfoList object to be written by \a
+ //! module_list.
+ //!
+ //! This object takes ownership of \a module_list and becomes its parent in
+ //! the overall tree of internal::MinidumpWritable objects.
+ //!
+ //! \note Valid in #kStateMutable.
+ void SetModuleList(
+ scoped_ptr<MinidumpModuleCrashpadInfoListWriter> module_list);
+
+ //! \brief Determines whether the object is useful.
+ //!
+ //! A useful object is one that carries data that makes a meaningful
+ //! contribution to a minidump file. An object carrying children would be
+ //! considered useful.
+ //!
+ //! \return `true` if the object is useful, `false` otherwise.
+ bool IsUseful() const;
+
+ protected:
+ // MinidumpWritable:
+ bool Freeze() override;
+ size_t SizeOfObject() override;
+ std::vector<MinidumpWritable*> Children() override;
+ bool WriteObject(FileWriterInterface* file_writer) override;
+
+ // MinidumpStreamWriter:
+ MinidumpStreamType StreamType() const override;
+
+ private:
+ MinidumpCrashpadInfo crashpad_info_;
+ scoped_ptr<MinidumpSimpleStringDictionaryWriter> simple_annotations_;
+ scoped_ptr<MinidumpModuleCrashpadInfoListWriter> module_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpCrashpadInfoWriter);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_MINIDUMP_MINIDUMP_CRASHPAD_INFO_WRITER_H_
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_crashpad_info_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_crashpad_info_writer_test.cc
new file mode 100644
index 00000000000..06733edba54
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_crashpad_info_writer_test.cc
@@ -0,0 +1,315 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_crashpad_info_writer.h"
+
+#include <windows.h>
+#include <dbghelp.h>
+
+#include <map>
+#include <string>
+
+#include "gtest/gtest.h"
+#include "minidump/minidump_extensions.h"
+#include "minidump/minidump_file_writer.h"
+#include "minidump/minidump_module_crashpad_info_writer.h"
+#include "minidump/minidump_simple_string_dictionary_writer.h"
+#include "minidump/test/minidump_file_writer_test_util.h"
+#include "minidump/test/minidump_string_writer_test_util.h"
+#include "minidump/test/minidump_writable_test_util.h"
+#include "snapshot/test/test_module_snapshot.h"
+#include "snapshot/test/test_process_snapshot.h"
+#include "util/file/string_file.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+void GetCrashpadInfoStream(
+ const std::string& file_contents,
+ const MinidumpCrashpadInfo** crashpad_info,
+ const MinidumpSimpleStringDictionary** simple_annotations,
+ const MinidumpModuleCrashpadInfoList** module_list) {
+ const MINIDUMP_DIRECTORY* directory;
+ const MINIDUMP_HEADER* header =
+ MinidumpHeaderAtStart(file_contents, &directory);
+ ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));
+ ASSERT_TRUE(directory);
+
+ ASSERT_EQ(kMinidumpStreamTypeCrashpadInfo, directory[0].StreamType);
+
+ *crashpad_info = MinidumpWritableAtLocationDescriptor<MinidumpCrashpadInfo>(
+ file_contents, directory[0].Location);
+ ASSERT_TRUE(*crashpad_info);
+
+ *simple_annotations =
+ MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(
+ file_contents, (*crashpad_info)->simple_annotations);
+
+ *module_list =
+ MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfoList>(
+ file_contents, (*crashpad_info)->module_list);
+}
+
+TEST(MinidumpCrashpadInfoWriter, Empty) {
+ MinidumpFileWriter minidump_file_writer;
+ auto crashpad_info_writer = make_scoped_ptr(new MinidumpCrashpadInfoWriter());
+ EXPECT_FALSE(crashpad_info_writer->IsUseful());
+
+ minidump_file_writer.AddStream(crashpad_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MinidumpCrashpadInfo* crashpad_info = nullptr;
+ const MinidumpSimpleStringDictionary* simple_annotations = nullptr;
+ const MinidumpModuleCrashpadInfoList* module_list = nullptr;
+
+ ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream(
+ string_file.string(), &crashpad_info, &simple_annotations, &module_list));
+
+ EXPECT_EQ(MinidumpCrashpadInfo::kVersion, crashpad_info->version);
+ EXPECT_EQ(UUID(), crashpad_info->report_id);
+ EXPECT_EQ(UUID(), crashpad_info->client_id);
+ EXPECT_FALSE(simple_annotations);
+ EXPECT_FALSE(module_list);
+}
+
+TEST(MinidumpCrashpadInfoWriter, ReportAndClientID) {
+ MinidumpFileWriter minidump_file_writer;
+ auto crashpad_info_writer = make_scoped_ptr(new MinidumpCrashpadInfoWriter());
+
+ UUID report_id;
+ ASSERT_TRUE(
+ report_id.InitializeFromString("01234567-89ab-cdef-0123-456789abcdef"));
+ crashpad_info_writer->SetReportID(report_id);
+
+ UUID client_id;
+ ASSERT_TRUE(
+ client_id.InitializeFromString("00112233-4455-6677-8899-aabbccddeeff"));
+ crashpad_info_writer->SetClientID(client_id);
+
+ EXPECT_TRUE(crashpad_info_writer->IsUseful());
+
+ minidump_file_writer.AddStream(crashpad_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MinidumpCrashpadInfo* crashpad_info = nullptr;
+ const MinidumpSimpleStringDictionary* simple_annotations = nullptr;
+ const MinidumpModuleCrashpadInfoList* module_list = nullptr;
+
+ ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream(
+ string_file.string(), &crashpad_info, &simple_annotations, &module_list));
+
+ EXPECT_EQ(MinidumpCrashpadInfo::kVersion, crashpad_info->version);
+ EXPECT_EQ(report_id, crashpad_info->report_id);
+ EXPECT_EQ(client_id, crashpad_info->client_id);
+ EXPECT_FALSE(simple_annotations);
+ EXPECT_FALSE(module_list);
+}
+
+TEST(MinidumpCrashpadInfoWriter, SimpleAnnotations) {
+ MinidumpFileWriter minidump_file_writer;
+ auto crashpad_info_writer = make_scoped_ptr(new MinidumpCrashpadInfoWriter());
+
+ const char kKey[] =
+ "a thing that provides a means of gaining access to or understanding "
+ "something";
+ const char kValue[] =
+ "the numerical amount denoted by an algebraic term; a magnitude, "
+ "quantity, or number";
+ auto simple_string_dictionary_writer =
+ make_scoped_ptr(new MinidumpSimpleStringDictionaryWriter());
+ auto simple_string_dictionary_entry_writer =
+ make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter());
+ simple_string_dictionary_entry_writer->SetKeyValue(kKey, kValue);
+ simple_string_dictionary_writer->AddEntry(
+ simple_string_dictionary_entry_writer.Pass());
+ crashpad_info_writer->SetSimpleAnnotations(
+ simple_string_dictionary_writer.Pass());
+
+ EXPECT_TRUE(crashpad_info_writer->IsUseful());
+
+ minidump_file_writer.AddStream(crashpad_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MinidumpCrashpadInfo* crashpad_info = nullptr;
+ const MinidumpSimpleStringDictionary* simple_annotations = nullptr;
+ const MinidumpModuleCrashpadInfoList* module_list = nullptr;
+
+ ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream(
+ string_file.string(), &crashpad_info, &simple_annotations, &module_list));
+
+ EXPECT_EQ(MinidumpCrashpadInfo::kVersion, crashpad_info->version);
+ EXPECT_FALSE(module_list);
+
+ ASSERT_TRUE(simple_annotations);
+ ASSERT_EQ(1u, simple_annotations->count);
+ EXPECT_EQ(kKey,
+ MinidumpUTF8StringAtRVAAsString(
+ string_file.string(), simple_annotations->entries[0].key));
+ EXPECT_EQ(kValue,
+ MinidumpUTF8StringAtRVAAsString(
+ string_file.string(), simple_annotations->entries[0].value));
+}
+
+TEST(MinidumpCrashpadInfoWriter, CrashpadModuleList) {
+ const uint32_t kMinidumpModuleListIndex = 3;
+
+ MinidumpFileWriter minidump_file_writer;
+ auto crashpad_info_writer = make_scoped_ptr(new MinidumpCrashpadInfoWriter());
+
+ auto module_list_writer =
+ make_scoped_ptr(new MinidumpModuleCrashpadInfoListWriter());
+ auto module_writer = make_scoped_ptr(new MinidumpModuleCrashpadInfoWriter());
+ module_list_writer->AddModule(module_writer.Pass(), kMinidumpModuleListIndex);
+ crashpad_info_writer->SetModuleList(module_list_writer.Pass());
+
+ EXPECT_TRUE(crashpad_info_writer->IsUseful());
+
+ minidump_file_writer.AddStream(crashpad_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MinidumpCrashpadInfo* crashpad_info = nullptr;
+ const MinidumpSimpleStringDictionary* simple_annotations = nullptr;
+ const MinidumpModuleCrashpadInfoList* module_list = nullptr;
+
+ ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream(
+ string_file.string(), &crashpad_info, &simple_annotations, &module_list));
+
+ EXPECT_EQ(MinidumpCrashpadInfo::kVersion, crashpad_info->version);
+ EXPECT_FALSE(simple_annotations);
+
+ ASSERT_TRUE(module_list);
+ ASSERT_EQ(1u, module_list->count);
+
+ EXPECT_EQ(kMinidumpModuleListIndex,
+ module_list->modules[0].minidump_module_list_index);
+ const MinidumpModuleCrashpadInfo* module =
+ MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>(
+ string_file.string(), module_list->modules[0].location);
+ ASSERT_TRUE(module);
+
+ EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module->version);
+ EXPECT_EQ(0u, module->list_annotations.DataSize);
+ EXPECT_EQ(0u, module->list_annotations.Rva);
+ EXPECT_EQ(0u, module->simple_annotations.DataSize);
+ EXPECT_EQ(0u, module->simple_annotations.Rva);
+}
+
+TEST(MinidumpCrashpadInfoWriter, InitializeFromSnapshot) {
+ UUID report_id;
+ ASSERT_TRUE(
+ report_id.InitializeFromString("fedcba98-7654-3210-fedc-ba9876543210"));
+
+ UUID client_id;
+ ASSERT_TRUE(
+ client_id.InitializeFromString("fedcba98-7654-3210-0123-456789abcdef"));
+
+ const char kKey[] = "version";
+ const char kValue[] = "40.0.2214.111";
+ const char kEntry[] = "This is a simple annotation in a list.";
+
+ // Test with a useless module, one that doesn’t carry anything that would
+ // require MinidumpCrashpadInfo or any child object.
+ auto process_snapshot = make_scoped_ptr(new TestProcessSnapshot());
+
+ auto module_snapshot = make_scoped_ptr(new TestModuleSnapshot());
+ process_snapshot->AddModule(module_snapshot.Pass());
+
+ auto info_writer = make_scoped_ptr(new MinidumpCrashpadInfoWriter());
+ info_writer->InitializeFromSnapshot(process_snapshot.get());
+ EXPECT_FALSE(info_writer->IsUseful());
+
+ // Try again with a useful module.
+ process_snapshot.reset(new TestProcessSnapshot());
+
+ process_snapshot->SetReportID(report_id);
+ process_snapshot->SetClientID(client_id);
+
+ std::map<std::string, std::string> annotations_simple_map;
+ annotations_simple_map[kKey] = kValue;
+ process_snapshot->SetAnnotationsSimpleMap(annotations_simple_map);
+
+ module_snapshot.reset(new TestModuleSnapshot());
+ std::vector<std::string> annotations_list(1, std::string(kEntry));
+ module_snapshot->SetAnnotationsVector(annotations_list);
+ process_snapshot->AddModule(module_snapshot.Pass());
+
+ info_writer.reset(new MinidumpCrashpadInfoWriter());
+ info_writer->InitializeFromSnapshot(process_snapshot.get());
+ EXPECT_TRUE(info_writer->IsUseful());
+
+ MinidumpFileWriter minidump_file_writer;
+ minidump_file_writer.AddStream(info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MinidumpCrashpadInfo* info = nullptr;
+ const MinidumpSimpleStringDictionary* simple_annotations;
+ const MinidumpModuleCrashpadInfoList* module_list;
+ ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream(
+ string_file.string(), &info, &simple_annotations, &module_list));
+
+ EXPECT_EQ(MinidumpCrashpadInfo::kVersion, info->version);
+
+ EXPECT_EQ(report_id, info->report_id);
+ EXPECT_EQ(client_id, info->client_id);
+
+ ASSERT_TRUE(simple_annotations);
+ ASSERT_EQ(1u, simple_annotations->count);
+ EXPECT_EQ(kKey,
+ MinidumpUTF8StringAtRVAAsString(
+ string_file.string(), simple_annotations->entries[0].key));
+ EXPECT_EQ(kValue,
+ MinidumpUTF8StringAtRVAAsString(
+ string_file.string(), simple_annotations->entries[0].value));
+
+ ASSERT_TRUE(module_list);
+ ASSERT_EQ(1u, module_list->count);
+
+ EXPECT_EQ(0u, module_list->modules[0].minidump_module_list_index);
+ const MinidumpModuleCrashpadInfo* module =
+ MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>(
+ string_file.string(), module_list->modules[0].location);
+ ASSERT_TRUE(module);
+
+ EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module->version);
+
+ const MinidumpRVAList* list_annotations =
+ MinidumpWritableAtLocationDescriptor<MinidumpRVAList>(
+ string_file.string(), module->list_annotations);
+ ASSERT_TRUE(list_annotations);
+
+ ASSERT_EQ(1u, list_annotations->count);
+ EXPECT_EQ(kEntry,
+ MinidumpUTF8StringAtRVAAsString(string_file.string(),
+ list_annotations->children[0]));
+
+ const MinidumpSimpleStringDictionary* module_simple_annotations =
+ MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(
+ string_file.string(), module->simple_annotations);
+ EXPECT_FALSE(module_simple_annotations);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_exception_writer.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_exception_writer.cc
new file mode 100644
index 00000000000..3d0c3414d08
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_exception_writer.cc
@@ -0,0 +1,121 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_exception_writer.h"
+
+#include <sys/types.h>
+
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "minidump/minidump_context_writer.h"
+#include "snapshot/exception_snapshot.h"
+#include "util/file/file_writer.h"
+
+namespace crashpad {
+
+MinidumpExceptionWriter::MinidumpExceptionWriter()
+ : MinidumpStreamWriter(), exception_(), context_(nullptr) {
+}
+
+MinidumpExceptionWriter::~MinidumpExceptionWriter() {
+}
+
+void MinidumpExceptionWriter::InitializeFromSnapshot(
+ const ExceptionSnapshot* exception_snapshot,
+ const MinidumpThreadIDMap& thread_id_map) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK(!context_);
+
+ auto thread_id_it = thread_id_map.find(exception_snapshot->ThreadID());
+ DCHECK(thread_id_it != thread_id_map.end());
+ SetThreadID(thread_id_it->second);
+
+ SetExceptionCode(exception_snapshot->Exception());
+ SetExceptionFlags(exception_snapshot->ExceptionInfo());
+ SetExceptionAddress(exception_snapshot->ExceptionAddress());
+ SetExceptionInformation(exception_snapshot->Codes());
+
+ scoped_ptr<MinidumpContextWriter> context =
+ MinidumpContextWriter::CreateFromSnapshot(exception_snapshot->Context());
+ SetContext(context.Pass());
+}
+
+void MinidumpExceptionWriter::SetContext(
+ scoped_ptr<MinidumpContextWriter> context) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ context_ = context.Pass();
+}
+
+void MinidumpExceptionWriter::SetExceptionInformation(
+ const std::vector<uint64_t>& exception_information) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ const size_t parameters = exception_information.size();
+ const size_t kMaxParameters =
+ arraysize(exception_.ExceptionRecord.ExceptionInformation);
+ CHECK_LE(parameters, kMaxParameters);
+
+ exception_.ExceptionRecord.NumberParameters =
+ base::checked_cast<uint32_t>(parameters);
+ size_t parameter = 0;
+ for (; parameter < parameters; ++parameter) {
+ exception_.ExceptionRecord.ExceptionInformation[parameter] =
+ exception_information[parameter];
+ }
+ for (; parameter < kMaxParameters; ++parameter) {
+ exception_.ExceptionRecord.ExceptionInformation[parameter] = 0;
+ }
+}
+
+bool MinidumpExceptionWriter::Freeze() {
+ DCHECK_EQ(state(), kStateMutable);
+ CHECK(context_);
+
+ if (!MinidumpStreamWriter::Freeze()) {
+ return false;
+ }
+
+ context_->RegisterLocationDescriptor(&exception_.ThreadContext);
+
+ return true;
+}
+
+size_t MinidumpExceptionWriter::SizeOfObject() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ return sizeof(exception_);
+}
+
+std::vector<internal::MinidumpWritable*> MinidumpExceptionWriter::Children() {
+ DCHECK_GE(state(), kStateFrozen);
+ DCHECK(context_);
+
+ std::vector<MinidumpWritable*> children;
+ children.push_back(context_.get());
+
+ return children;
+}
+
+bool MinidumpExceptionWriter::WriteObject(FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateWritable);
+
+ return file_writer->Write(&exception_, sizeof(exception_));
+}
+
+MinidumpStreamType MinidumpExceptionWriter::StreamType() const {
+ return kMinidumpStreamTypeException;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_exception_writer.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_exception_writer.h
new file mode 100644
index 00000000000..b4de21a5b7c
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_exception_writer.h
@@ -0,0 +1,124 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_MINIDUMP_MINIDUMP_EXCEPTION_WRITER_H_
+#define CRASHPAD_MINIDUMP_MINIDUMP_EXCEPTION_WRITER_H_
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "minidump/minidump_stream_writer.h"
+#include "minidump/minidump_thread_id_map.h"
+
+namespace crashpad {
+
+class ExceptionSnapshot;
+class MinidumpContextWriter;
+
+//! \brief The writer for a MINIDUMP_EXCEPTION_STREAM stream in a minidump file.
+class MinidumpExceptionWriter final : public internal::MinidumpStreamWriter {
+ public:
+ MinidumpExceptionWriter();
+ ~MinidumpExceptionWriter() override;
+
+ //! \brief Initializes the MINIDUMP_EXCEPTION_STREAM based on \a
+ //! exception_snapshot.
+ //!
+ //! \param[in] exception_snapshot The exception snapshot to use as source
+ //! data.
+ //! \param[in] thread_id_map A MinidumpThreadIDMap to be consulted to
+ //! determine the 32-bit minidump thread ID to use for the thread
+ //! identified by \a exception_snapshot.
+ //!
+ //! \note Valid in #kStateMutable. No mutator methods may be called before
+ //! this method, and it is not normally necessary to call any mutator
+ //! methods after this method.
+ void InitializeFromSnapshot(const ExceptionSnapshot* exception_snapshot,
+ const MinidumpThreadIDMap& thread_id_map);
+
+ //! \brief Arranges for MINIDUMP_EXCEPTION_STREAM::ThreadContext to point to
+ //! the CPU context to be written by \a context.
+ //!
+ //! A context is required in all MINIDUMP_EXCEPTION_STREAM objects.
+ //!
+ //! This object takes ownership of \a context and becomes its parent in the
+ //! overall tree of internal::MinidumpWritable objects.
+ //!
+ //! \note Valid in #kStateMutable.
+ void SetContext(scoped_ptr<MinidumpContextWriter> context);
+
+ //! \brief Sets MINIDUMP_EXCEPTION_STREAM::ThreadId.
+ void SetThreadID(uint32_t thread_id) { exception_.ThreadId = thread_id; }
+
+ //! \brief Sets MINIDUMP_EXCEPTION::ExceptionCode.
+ void SetExceptionCode(uint32_t exception_code) {
+ exception_.ExceptionRecord.ExceptionCode = exception_code;
+ }
+
+ //! \brief Sets MINIDUMP_EXCEPTION::ExceptionFlags.
+ void SetExceptionFlags(uint32_t exception_flags) {
+ exception_.ExceptionRecord.ExceptionFlags = exception_flags;
+ }
+
+ //! \brief Sets MINIDUMP_EXCEPTION::ExceptionRecord.
+ void SetExceptionRecord(uint64_t exception_record) {
+ exception_.ExceptionRecord.ExceptionRecord = exception_record;
+ }
+
+ //! \brief Sets MINIDUMP_EXCEPTION::ExceptionAddress.
+ void SetExceptionAddress(uint64_t exception_address) {
+ exception_.ExceptionRecord.ExceptionAddress = exception_address;
+ }
+
+ //! \brief Sets MINIDUMP_EXCEPTION::ExceptionInformation and
+ //! MINIDUMP_EXCEPTION::NumberParameters.
+ //!
+ //! MINIDUMP_EXCEPTION::NumberParameters is set to the number of elements in
+ //! \a exception_information. The elements of
+ //! MINIDUMP_EXCEPTION::ExceptionInformation are set to the elements of \a
+ //! exception_information. Unused elements in
+ //! MINIDUMP_EXCEPTION::ExceptionInformation are set to `0`.
+ //!
+ //! \a exception_information must have no more than
+ //! #EXCEPTION_MAXIMUM_PARAMETERS elements.
+ //!
+ //! \note Valid in #kStateMutable.
+ void SetExceptionInformation(
+ const std::vector<uint64_t>& exception_information);
+
+ protected:
+ // MinidumpWritable:
+ bool Freeze() override;
+ size_t SizeOfObject() override;
+ std::vector<MinidumpWritable*> Children() override;
+ bool WriteObject(FileWriterInterface* file_writer) override;
+
+ // MinidumpStreamWriter:
+ MinidumpStreamType StreamType() const override;
+
+ private:
+ MINIDUMP_EXCEPTION_STREAM exception_;
+ scoped_ptr<MinidumpContextWriter> context_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpExceptionWriter);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_MINIDUMP_MINIDUMP_EXCEPTION_WRITER_H_
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_exception_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_exception_writer_test.cc
new file mode 100644
index 00000000000..881c36eb17f
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_exception_writer_test.cc
@@ -0,0 +1,281 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_exception_writer.h"
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "minidump/minidump_context.h"
+#include "minidump/minidump_context_writer.h"
+#include "minidump/minidump_extensions.h"
+#include "minidump/minidump_file_writer.h"
+#include "minidump/minidump_thread_id_map.h"
+#include "minidump/test/minidump_context_test_util.h"
+#include "minidump/test/minidump_file_writer_test_util.h"
+#include "minidump/test/minidump_writable_test_util.h"
+#include "snapshot/test/test_cpu_context.h"
+#include "snapshot/test/test_exception_snapshot.h"
+#include "test/gtest_death_check.h"
+#include "util/file/string_file.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+// This returns the MINIDUMP_EXCEPTION_STREAM stream in |exception_stream|.
+void GetExceptionStream(const std::string& file_contents,
+ const MINIDUMP_EXCEPTION_STREAM** exception_stream) {
+ const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
+ const size_t kExceptionStreamOffset =
+ kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);
+ const size_t kContextOffset =
+ kExceptionStreamOffset + sizeof(MINIDUMP_EXCEPTION_STREAM);
+ const size_t kFileSize = kContextOffset + sizeof(MinidumpContextX86);
+ ASSERT_EQ(file_contents.size(), kFileSize);
+
+ const MINIDUMP_DIRECTORY* directory;
+ const MINIDUMP_HEADER* header =
+ MinidumpHeaderAtStart(file_contents, &directory);
+ ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));
+
+ ASSERT_EQ(kMinidumpStreamTypeException, directory[0].StreamType);
+ EXPECT_EQ(kExceptionStreamOffset, directory[0].Location.Rva);
+
+ *exception_stream =
+ MinidumpWritableAtLocationDescriptor<MINIDUMP_EXCEPTION_STREAM>(
+ file_contents, directory[0].Location);
+ ASSERT_TRUE(exception_stream);
+}
+
+// The MINIDUMP_EXCEPTION_STREAMs |expected| and |observed| are compared against
+// each other using gtest assertions. The context will be recovered from
+// |file_contents| and stored in |context|.
+void ExpectExceptionStream(const MINIDUMP_EXCEPTION_STREAM* expected,
+ const MINIDUMP_EXCEPTION_STREAM* observed,
+ const std::string& file_contents,
+ const MinidumpContextX86** context) {
+ EXPECT_EQ(expected->ThreadId, observed->ThreadId);
+ EXPECT_EQ(0u, observed->__alignment);
+ EXPECT_EQ(expected->ExceptionRecord.ExceptionCode,
+ observed->ExceptionRecord.ExceptionCode);
+ EXPECT_EQ(expected->ExceptionRecord.ExceptionFlags,
+ observed->ExceptionRecord.ExceptionFlags);
+ EXPECT_EQ(expected->ExceptionRecord.ExceptionRecord,
+ observed->ExceptionRecord.ExceptionRecord);
+ EXPECT_EQ(expected->ExceptionRecord.ExceptionAddress,
+ observed->ExceptionRecord.ExceptionAddress);
+ EXPECT_EQ(expected->ExceptionRecord.NumberParameters,
+ observed->ExceptionRecord.NumberParameters);
+ EXPECT_EQ(0u, observed->ExceptionRecord.__unusedAlignment);
+ for (size_t index = 0;
+ index < arraysize(observed->ExceptionRecord.ExceptionInformation);
+ ++index) {
+ EXPECT_EQ(expected->ExceptionRecord.ExceptionInformation[index],
+ observed->ExceptionRecord.ExceptionInformation[index]);
+ }
+ *context = MinidumpWritableAtLocationDescriptor<MinidumpContextX86>(
+ file_contents, observed->ThreadContext);
+ ASSERT_TRUE(context);
+}
+
+TEST(MinidumpExceptionWriter, Minimal) {
+ MinidumpFileWriter minidump_file_writer;
+ auto exception_writer = make_scoped_ptr(new MinidumpExceptionWriter());
+
+ const uint32_t kSeed = 100;
+
+ auto context_x86_writer = make_scoped_ptr(new MinidumpContextX86Writer());
+ InitializeMinidumpContextX86(context_x86_writer->context(), kSeed);
+ exception_writer->SetContext(context_x86_writer.Pass());
+
+ minidump_file_writer.AddStream(exception_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_EXCEPTION_STREAM* observed_exception_stream = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ GetExceptionStream(string_file.string(), &observed_exception_stream));
+
+ MINIDUMP_EXCEPTION_STREAM expected_exception_stream = {};
+ expected_exception_stream.ThreadContext.DataSize = sizeof(MinidumpContextX86);
+
+ const MinidumpContextX86* observed_context = nullptr;
+ ASSERT_NO_FATAL_FAILURE(ExpectExceptionStream(&expected_exception_stream,
+ observed_exception_stream,
+ string_file.string(),
+ &observed_context));
+
+ ASSERT_NO_FATAL_FAILURE(
+ ExpectMinidumpContextX86(kSeed, observed_context, false));
+}
+
+TEST(MinidumpExceptionWriter, Standard) {
+ MinidumpFileWriter minidump_file_writer;
+ auto exception_writer = make_scoped_ptr(new MinidumpExceptionWriter());
+
+ const uint32_t kSeed = 200;
+ const uint32_t kThreadID = 1;
+ const uint32_t kExceptionCode = 2;
+ const uint32_t kExceptionFlags = 3;
+ const uint32_t kExceptionRecord = 4;
+ const uint32_t kExceptionAddress = 5;
+ const uint64_t kExceptionInformation0 = 6;
+ const uint64_t kExceptionInformation1 = 7;
+ const uint64_t kExceptionInformation2 = 7;
+
+ auto context_x86_writer = make_scoped_ptr(new MinidumpContextX86Writer());
+ InitializeMinidumpContextX86(context_x86_writer->context(), kSeed);
+ exception_writer->SetContext(context_x86_writer.Pass());
+
+ exception_writer->SetThreadID(kThreadID);
+ exception_writer->SetExceptionCode(kExceptionCode);
+ exception_writer->SetExceptionFlags(kExceptionFlags);
+ exception_writer->SetExceptionRecord(kExceptionRecord);
+ exception_writer->SetExceptionAddress(kExceptionAddress);
+
+ // Set a lot of exception information at first, and then replace it with less.
+ // This tests that the exception that is written does not contain the
+ // “garbage” from the initial SetExceptionInformation() call.
+ std::vector<uint64_t> exception_information(EXCEPTION_MAXIMUM_PARAMETERS,
+ 0x5a5a5a5a5a5a5a5a);
+ exception_writer->SetExceptionInformation(exception_information);
+
+ exception_information.clear();
+ exception_information.push_back(kExceptionInformation0);
+ exception_information.push_back(kExceptionInformation1);
+ exception_information.push_back(kExceptionInformation2);
+ exception_writer->SetExceptionInformation(exception_information);
+
+ minidump_file_writer.AddStream(exception_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_EXCEPTION_STREAM* observed_exception_stream = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ GetExceptionStream(string_file.string(), &observed_exception_stream));
+
+ MINIDUMP_EXCEPTION_STREAM expected_exception_stream = {};
+ expected_exception_stream.ThreadId = kThreadID;
+ expected_exception_stream.ExceptionRecord.ExceptionCode = kExceptionCode;
+ expected_exception_stream.ExceptionRecord.ExceptionFlags = kExceptionFlags;
+ expected_exception_stream.ExceptionRecord.ExceptionRecord = kExceptionRecord;
+ expected_exception_stream.ExceptionRecord.ExceptionAddress =
+ kExceptionAddress;
+ expected_exception_stream.ExceptionRecord.NumberParameters =
+ static_cast<uint32_t>(exception_information.size());
+ for (size_t index = 0; index < exception_information.size(); ++index) {
+ expected_exception_stream.ExceptionRecord.ExceptionInformation[index] =
+ exception_information[index];
+ }
+ expected_exception_stream.ThreadContext.DataSize = sizeof(MinidumpContextX86);
+
+ const MinidumpContextX86* observed_context = nullptr;
+ ASSERT_NO_FATAL_FAILURE(ExpectExceptionStream(&expected_exception_stream,
+ observed_exception_stream,
+ string_file.string(),
+ &observed_context));
+
+ ASSERT_NO_FATAL_FAILURE(
+ ExpectMinidumpContextX86(kSeed, observed_context, false));
+}
+
+TEST(MinidumpExceptionWriter, InitializeFromSnapshot) {
+ std::vector<uint64_t> exception_codes;
+ exception_codes.push_back(0x1000000000000000);
+ exception_codes.push_back(0x5555555555555555);
+
+ MINIDUMP_EXCEPTION_STREAM expect_exception = {};
+
+ expect_exception.ThreadId = 123;
+ expect_exception.ExceptionRecord.ExceptionCode = 100;
+ expect_exception.ExceptionRecord.ExceptionFlags = 1;
+ expect_exception.ExceptionRecord.ExceptionAddress = 0xfedcba9876543210;
+ expect_exception.ExceptionRecord.NumberParameters =
+ static_cast<uint32_t>(exception_codes.size());
+ for (size_t index = 0; index < exception_codes.size(); ++index) {
+ expect_exception.ExceptionRecord.ExceptionInformation[index] =
+ exception_codes[index];
+ }
+ const uint64_t kThreadID = 0xaaaaaaaaaaaaaaaa;
+ const uint32_t kSeed = 65;
+
+ TestExceptionSnapshot exception_snapshot;
+ exception_snapshot.SetThreadID(kThreadID);
+ exception_snapshot.SetException(
+ expect_exception.ExceptionRecord.ExceptionCode);
+ exception_snapshot.SetExceptionInfo(
+ expect_exception.ExceptionRecord.ExceptionFlags);
+ exception_snapshot.SetExceptionAddress(
+ expect_exception.ExceptionRecord.ExceptionAddress);
+ exception_snapshot.SetCodes(exception_codes);
+
+ InitializeCPUContextX86(exception_snapshot.MutableContext(), kSeed);
+
+ MinidumpThreadIDMap thread_id_map;
+ thread_id_map[kThreadID] = expect_exception.ThreadId;
+
+ auto exception_writer = make_scoped_ptr(new MinidumpExceptionWriter());
+ exception_writer->InitializeFromSnapshot(&exception_snapshot, thread_id_map);
+
+ MinidumpFileWriter minidump_file_writer;
+ minidump_file_writer.AddStream(exception_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_EXCEPTION_STREAM* exception = nullptr;
+ ASSERT_NO_FATAL_FAILURE(GetExceptionStream(string_file.string(), &exception));
+
+ const MinidumpContextX86* observed_context = nullptr;
+ ASSERT_NO_FATAL_FAILURE(ExpectExceptionStream(&expect_exception,
+ exception,
+ string_file.string(),
+ &observed_context));
+
+ ASSERT_NO_FATAL_FAILURE(
+ ExpectMinidumpContextX86(kSeed, observed_context, true));
+}
+
+TEST(MinidumpExceptionWriterDeathTest, NoContext) {
+ MinidumpFileWriter minidump_file_writer;
+ auto exception_writer = make_scoped_ptr(new MinidumpExceptionWriter());
+
+ minidump_file_writer.AddStream(exception_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_DEATH_CHECK(minidump_file_writer.WriteEverything(&string_file),
+ "context_");
+}
+
+TEST(MinidumpExceptionWriterDeathTest, TooMuchInformation) {
+ MinidumpExceptionWriter exception_writer;
+ std::vector<uint64_t> exception_information(EXCEPTION_MAXIMUM_PARAMETERS + 1,
+ 0x5a5a5a5a5a5a5a5a);
+ ASSERT_DEATH_CHECK(
+ exception_writer.SetExceptionInformation(exception_information),
+ "kMaxParameters");
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_extensions.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_extensions.cc
new file mode 100644
index 00000000000..da67d0c5c39
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_extensions.cc
@@ -0,0 +1,24 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_extensions.h"
+
+namespace crashpad {
+
+const uint32_t MinidumpModuleCodeViewRecordPDB20::kSignature;
+const uint32_t MinidumpModuleCodeViewRecordPDB70::kSignature;
+const uint32_t MinidumpModuleCrashpadInfo::kVersion;
+const uint32_t MinidumpCrashpadInfo::kVersion;
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_extensions.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_extensions.h
new file mode 100644
index 00000000000..b8bf77a1c4a
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_extensions.h
@@ -0,0 +1,531 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_MINIDUMP_MINIDUMP_EXTENSIONS_H_
+#define CRASHPAD_MINIDUMP_MINIDUMP_EXTENSIONS_H_
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <stdint.h>
+#include <winnt.h>
+
+#include "base/compiler_specific.h"
+#include "build/build_config.h"
+#include "util/misc/uuid.h"
+
+// C4200 is "nonstandard extension used : zero-sized array in struct/union".
+// We would like to globally disable this warning, but unfortunately, the
+// compiler is buggy and only supports disabling it with a pragma, so we can't
+// disable it with other silly warnings in build/common.gypi. See:
+// https://connect.microsoft.com/VisualStudio/feedback/details/1114440
+MSVC_PUSH_DISABLE_WARNING(4200);
+
+#if defined(COMPILER_MSVC)
+#define PACKED
+#pragma pack(push, 1)
+#else
+#define PACKED __attribute__((packed))
+#endif // COMPILER_MSVC
+
+namespace crashpad {
+
+//! \brief Minidump stream type values for MINIDUMP_DIRECTORY::StreamType. Each
+//! stream structure has a corresponding stream type value to identify it.
+//!
+//! \sa MINIDUMP_STREAM_TYPE
+enum MinidumpStreamType : uint32_t {
+ //! \brief The stream type for MINIDUMP_THREAD_LIST.
+ //!
+ //! \sa ThreadListStream
+ kMinidumpStreamTypeThreadList = ThreadListStream,
+
+ //! \brief The stream type for MINIDUMP_MODULE_LIST.
+ //!
+ //! \sa ModuleListStream
+ kMinidumpStreamTypeModuleList = ModuleListStream,
+
+ //! \brief The stream type for MINIDUMP_MEMORY_LIST.
+ //!
+ //! \sa MemoryListStream
+ kMinidumpStreamTypeMemoryList = MemoryListStream,
+
+ //! \brief The stream type for MINIDUMP_EXCEPTION_STREAM.
+ //!
+ //! \sa ExceptionStream
+ kMinidumpStreamTypeException = ExceptionStream,
+
+ //! \brief The stream type for MINIDUMP_SYSTEM_INFO.
+ //!
+ //! \sa SystemInfoStream
+ kMinidumpStreamTypeSystemInfo = SystemInfoStream,
+
+ //! \brief The stream type for MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2,
+ //! MINIDUMP_MISC_INFO_3, and MINIDUMP_MISC_INFO_4.
+ //!
+ //! \sa MiscInfoStream
+ kMinidumpStreamTypeMiscInfo = MiscInfoStream,
+
+ // 0x4350 = "CP"
+
+ //! \brief The stream type for MinidumpCrashpadInfo.
+ kMinidumpStreamTypeCrashpadInfo = 0x43500001,
+};
+
+//! \brief A variable-length UTF-8-encoded string carried within a minidump
+//! file.
+//!
+//! \sa MINIDUMP_STRING
+struct ALIGNAS(4) PACKED MinidumpUTF8String {
+ // The field names do not conform to typical style, they match the names used
+ // in MINIDUMP_STRING. This makes it easier to operate on MINIDUMP_STRING (for
+ // UTF-16 strings) and MinidumpUTF8String using templates.
+
+ //! \brief The length of the #Buffer field in bytes, not including the `NUL`
+ //! terminator.
+ //!
+ //! \note This field is interpreted as a byte count, not a count of Unicode
+ //! code points.
+ uint32_t Length;
+
+ //! \brief The string, encoded in UTF-8, and terminated with a `NUL` byte.
+ uint8_t Buffer[0];
+};
+
+//! \brief CPU type values for MINIDUMP_SYSTEM_INFO::ProcessorArchitecture.
+//!
+//! \sa \ref PROCESSOR_ARCHITECTURE_x "PROCESSOR_ARCHITECTURE_*"
+enum MinidumpCPUArchitecture : uint16_t {
+ //! \brief 32-bit x86.
+ //!
+ //! These systems identify their CPUs generically as “x86” or “ia32”, or with
+ //! more specific names such as “i386”, “i486”, “i586”, and “i686”.
+ kMinidumpCPUArchitectureX86 = PROCESSOR_ARCHITECTURE_INTEL,
+
+ kMinidumpCPUArchitectureMIPS = PROCESSOR_ARCHITECTURE_MIPS,
+ kMinidumpCPUArchitectureAlpha = PROCESSOR_ARCHITECTURE_ALPHA,
+
+ //! \brief 32-bit PowerPC.
+ //!
+ //! These systems identify their CPUs generically as “ppc”, or with more
+ //! specific names such as “ppc6xx”, “ppc7xx”, and “ppc74xx”.
+ kMinidumpCPUArchitecturePPC = PROCESSOR_ARCHITECTURE_PPC,
+
+ kMinidumpCPUArchitectureSHx = PROCESSOR_ARCHITECTURE_SHX,
+
+ //! \brief 32-bit ARM.
+ //!
+ //! These systems identify their CPUs generically as “arm”, or with more
+ //! specific names such as “armv6” and “armv7”.
+ kMinidumpCPUArchitectureARM = PROCESSOR_ARCHITECTURE_ARM,
+
+ kMinidumpCPUArchitectureIA64 = PROCESSOR_ARCHITECTURE_IA64,
+ kMinidumpCPUArchitectureAlpha64 = PROCESSOR_ARCHITECTURE_ALPHA64,
+ kMinidumpCPUArchitectureMSIL = PROCESSOR_ARCHITECTURE_MSIL,
+
+ //! \brief 64-bit x86.
+ //!
+ //! These systems identify their CPUs as “x86_64”, “amd64”, or “x64”.
+ kMinidumpCPUArchitectureAMD64 = PROCESSOR_ARCHITECTURE_AMD64,
+
+ //! \brief A 32-bit x86 process running on IA-64 (Itanium).
+ //!
+ //! \note This value is not used in minidump files for 32-bit x86 processes
+ //! running on a 64-bit-capable x86 CPU and operating system. In that
+ //! configuration, #kMinidumpCPUArchitectureX86 is used instead.
+ kMinidumpCPUArchitectureX86Win64 = PROCESSOR_ARCHITECTURE_IA32_ON_WIN64,
+
+ kMinidumpCPUArchitectureNeutral = PROCESSOR_ARCHITECTURE_NEUTRAL,
+ kMinidumpCPUArchitectureSPARC = 0x8001,
+
+ //! \brief 64-bit PowerPC.
+ //!
+ //! These systems identify their CPUs generically as “ppc64”, or with more
+ //! specific names such as “ppc970”.
+ kMinidumpCPUArchitecturePPC64 = 0x8002,
+
+ //! \brief 64-bit ARM.
+ //!
+ //! These systems identify their CPUs generically as “arm64” or “aarch64”, or
+ //! with more specific names such as “armv8”.
+ kMinidumpCPUArchitectureARM64 = 0x8003,
+
+ //! \brief Unknown CPU architecture.
+ kMinidumpCPUArchitectureUnknown = PROCESSOR_ARCHITECTURE_UNKNOWN,
+};
+
+//! \brief Operating system type values for MINIDUMP_SYSTEM_INFO::ProductType.
+//!
+//! \sa \ref VER_NT_x "VER_NT_*"
+enum MinidumpOSType : uint8_t {
+ //! \brief A “desktop” or “workstation” system.
+ kMinidumpOSTypeWorkstation = VER_NT_WORKSTATION,
+
+ //! \brief A “domain controller” system. Windows-specific.
+ kMinidumpOSTypeDomainController = VER_NT_DOMAIN_CONTROLLER,
+
+ //! \brief A “server” system.
+ kMinidumpOSTypeServer = VER_NT_SERVER,
+};
+
+//! \brief Operating system family values for MINIDUMP_SYSTEM_INFO::PlatformId.
+//!
+//! \sa \ref VER_PLATFORM_x "VER_PLATFORM_*"
+enum MinidumpOS : uint32_t {
+ //! \brief Windows 3.1.
+ kMinidumpOSWin32s = VER_PLATFORM_WIN32s,
+
+ //! \brief Windows 95, Windows 98, and Windows Me.
+ kMinidumpOSWin32Windows = VER_PLATFORM_WIN32_WINDOWS,
+
+ //! \brief Windows NT, Windows 2000, and later.
+ kMinidumpOSWin32NT = VER_PLATFORM_WIN32_NT,
+
+ kMinidumpOSUnix = 0x8000,
+
+ //! \brief Mac OS X, Darwin for traditional systems.
+ kMinidumpOSMacOSX = 0x8101,
+
+ //! \brief iOS, Darwin for mobile devices.
+ kMinidumpOSiOS = 0x8102,
+
+ //! \brief Linux, not including Android.
+ kMinidumpOSLinux = 0x8201,
+
+ kMinidumpOSSolaris = 0x8202,
+
+ //! \brief Android.
+ kMinidumpOSAndroid = 0x8203,
+
+ kMinidumpOSPS3 = 0x8204,
+
+ //! \brief Native Client (NaCl).
+ kMinidumpOSNaCl = 0x8205,
+
+ //! \brief Unknown operating system.
+ kMinidumpOSUnknown = 0xffffffff,
+};
+
+//! \brief A CodeView record linking to a `.pdb` 2.0 file.
+//!
+//! This format provides an indirect link to debugging data by referencing an
+//! external `.pdb` file by its name, timestamp, and age. This structure may be
+//! pointed to by MINIDUMP_MODULE::CvRecord. It has been superseded by
+//! MinidumpModuleCodeViewRecordPDB70.
+//!
+//! For more information about this structure and format, see <a
+//! href="http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles">Matching
+//! Debug Information</a>, PDB Files, and <a
+//! href="http://undocumented.rawol.com/sbs-w2k-1-windows-2000-debugging-support.pdf#page=63">Undocumented
+//! Windows 2000 Secrets</a>, Windows 2000 Debugging Support/Microsoft Symbol
+//! File Internals/CodeView Subsections.
+//!
+//! \sa IMAGE_DEBUG_MISC
+struct MinidumpModuleCodeViewRecordPDB20 {
+ //! \brief The magic number identifying this structure version, stored in
+ //! #signature.
+ //!
+ //! In a hex dump, this will appear as “NB10” when produced by a little-endian
+ //! machine.
+ static const uint32_t kSignature = '01BN';
+
+ //! \brief The magic number identifying this structure version, the value of
+ //! #kSignature.
+ uint32_t signature;
+
+ //! \brief The offset to CodeView data.
+ //!
+ //! In this structure, this field always has the value `0` because no CodeView
+ //! data is present, there is only a link to CodeView data stored in an
+ //! external file.
+ uint32_t offset;
+
+ //! \brief The time that the `.pdb` file was created, in `time_t` format, the
+ //! number of seconds since the POSIX epoch.
+ uint32_t timestamp;
+
+ //! \brief The revision of the `.pdb` file.
+ //!
+ //! A `.pdb` file’s age indicates incremental changes to it. When a `.pdb`
+ //! file is created, it has age `1`, and subsequent updates increase this
+ //! value.
+ uint32_t age;
+
+ //! \brief The path or file name of the `.pdb` file associated with the
+ //! module.
+ //!
+ //! This is a NUL-terminated string. On Windows, it will be encoded in the
+ //! code page of the system that linked the module. On other operating
+ //! systems, UTF-8 may be used.
+ uint8_t pdb_name[1];
+};
+
+//! \brief A CodeView record linking to a `.pdb` 7.0 file.
+//!
+//! This format provides an indirect link to debugging data by referencing an
+//! external `.pdb` file by its name, %UUID, and age. This structure may be
+//! pointed to by MINIDUMP_MODULE::CvRecord.
+//!
+//! For more information about this structure and format, see <a
+//! href="http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles">Matching
+//! Debug Information</a>, PDB Files.
+//!
+//! \sa MinidumpModuleCodeViewRecordPDB20
+//! \sa IMAGE_DEBUG_MISC
+struct MinidumpModuleCodeViewRecordPDB70 {
+ // UUID has a constructor, which makes it non-POD, which makes this structure
+ // non-POD. In order for the default constructor to zero-initialize other
+ // members, an explicit constructor must be provided.
+ MinidumpModuleCodeViewRecordPDB70()
+ : signature(),
+ uuid(),
+ age(),
+ pdb_name() {
+ }
+
+ //! \brief The magic number identifying this structure version, stored in
+ //! #signature.
+ //!
+ //! In a hex dump, this will appear as “RSDS” when produced by a little-endian
+ //! machine.
+ static const uint32_t kSignature = 'SDSR';
+
+ //! \brief The magic number identifying this structure version, the value of
+ //! #kSignature.
+ uint32_t signature;
+
+ //! \brief The `.pdb` file’s unique identifier.
+ UUID uuid;
+
+ //! \brief The revision of the `.pdb` file.
+ //!
+ //! A `.pdb` file’s age indicates incremental changes to it. When a `.pdb`
+ //! file is created, it has age `1`, and subsequent updates increase this
+ //! value.
+ uint32_t age;
+
+ //! \brief The path or file name of the `.pdb` file associated with the
+ //! module.
+ //!
+ //! This is a NUL-terminated string. On Windows, it will be encoded in the
+ //! code page of the system that linked the module. On other operating
+ //! systems, UTF-8 may be used.
+ uint8_t pdb_name[1];
+};
+
+//! \brief A list of ::RVA pointers.
+struct ALIGNAS(4) PACKED MinidumpRVAList {
+ //! \brief The number of children present in the #children array.
+ uint32_t count;
+
+ //! \brief Pointers to other structures in the minidump file.
+ RVA children[0];
+};
+
+//! \brief A key-value pair.
+struct ALIGNAS(4) PACKED MinidumpSimpleStringDictionaryEntry {
+ //! \brief ::RVA of a MinidumpUTF8String containing the key of a key-value
+ //! pair.
+ RVA key;
+
+ //! \brief ::RVA of a MinidumpUTF8String containing the value of a key-value
+ //! pair.
+ RVA value;
+};
+
+//! \brief A list of key-value pairs.
+struct ALIGNAS(4) PACKED MinidumpSimpleStringDictionary {
+ //! \brief The number of key-value pairs present.
+ uint32_t count;
+
+ //! \brief A list of MinidumpSimpleStringDictionaryEntry entries.
+ MinidumpSimpleStringDictionaryEntry entries[0];
+};
+
+//! \brief Additional Crashpad-specific information about a module carried
+//! within a minidump file.
+//!
+//! This structure augments the information provided by MINIDUMP_MODULE. The
+//! minidump file must contain a module list stream
+//! (::kMinidumpStreamTypeModuleList) in order for this structure to appear.
+//!
+//! This structure is versioned. When changing this structure, leave the
+//! existing structure intact so that earlier parsers will be able to understand
+//! the fields they are aware of, and make additions at the end of the
+//! structure. Revise #kVersion and document each field’s validity based on
+//! #version, so that newer parsers will be able to determine whether the added
+//! fields are valid or not.
+//!
+//! \sa #MinidumpModuleCrashpadInfoList
+struct ALIGNAS(4) PACKED MinidumpModuleCrashpadInfo {
+ //! \brief The structure’s currently-defined version number.
+ //!
+ //! \sa version
+ static const uint32_t kVersion = 1;
+
+ //! \brief The structure’s version number.
+ //!
+ //! Readers can use this field to determine which other fields in the
+ //! structure are valid. Upon encountering a value greater than #kVersion, a
+ //! reader should assume that the structure’s layout is compatible with the
+ //! structure defined as having value #kVersion.
+ //!
+ //! Writers may produce values less than #kVersion in this field if there is
+ //! no need for any fields present in later versions.
+ uint32_t version;
+
+ //! \brief A MinidumpRVAList pointing to MinidumpUTF8String objects. The
+ //! module controls the data that appears here.
+ //!
+ //! These strings correspond to ModuleSnapshot::AnnotationsVector() and do not
+ //! duplicate anything in #simple_annotations.
+ //!
+ //! This field is present when #version is at least `1`.
+ MINIDUMP_LOCATION_DESCRIPTOR list_annotations;
+
+ //! \brief A MinidumpSimpleStringDictionary pointing to strings interpreted as
+ //! key-value pairs. The module controls the data that appears here.
+ //!
+ //! These key-value pairs correspond to
+ //! ModuleSnapshot::AnnotationsSimpleMap() and do not duplicate anything in
+ //! #list_annotations.
+ //!
+ //! This field is present when #version is at least `1`.
+ MINIDUMP_LOCATION_DESCRIPTOR simple_annotations;
+};
+
+//! \brief A link between a MINIDUMP_MODULE structure and additional
+//! Crashpad-specific information about a module carried within a minidump
+//! file.
+struct ALIGNAS(4) PACKED MinidumpModuleCrashpadInfoLink {
+ //! \brief A link to a MINIDUMP_MODULE structure in the module list stream.
+ //!
+ //! This field is an index into MINIDUMP_MODULE_LIST::Modules. This field’s
+ //! value must be in the range of MINIDUMP_MODULE_LIST::NumberOfEntries.
+ uint32_t minidump_module_list_index;
+
+ //! \brief A link to a MinidumpModuleCrashpadInfo structure.
+ //!
+ //! MinidumpModuleCrashpadInfo structures are accessed indirectly through
+ //! MINIDUMP_LOCATION_DESCRIPTOR pointers to allow for future growth of the
+ //! MinidumpModuleCrashpadInfo structure.
+ MINIDUMP_LOCATION_DESCRIPTOR location;
+};
+
+//! \brief Additional Crashpad-specific information about modules carried within
+//! a minidump file.
+//!
+//! This structure augments the information provided by
+//! MINIDUMP_MODULE_LIST. The minidump file must contain a module list stream
+//! (::kMinidumpStreamTypeModuleList) in order for this structure to appear.
+//!
+//! MinidumpModuleCrashpadInfoList::count may be less than the value of
+//! MINIDUMP_MODULE_LIST::NumberOfModules because not every MINIDUMP_MODULE
+//! structure carried within the minidump file will necessarily have
+//! Crashpad-specific information provided by a MinidumpModuleCrashpadInfo
+//! structure.
+struct ALIGNAS(4) PACKED MinidumpModuleCrashpadInfoList {
+ //! \brief The number of children present in the #modules array.
+ uint32_t count;
+
+ //! \brief Crashpad-specific information about modules, along with links to
+ //! MINIDUMP_MODULE structures that contain module information
+ //! traditionally carried within minidump files.
+ MinidumpModuleCrashpadInfoLink modules[0];
+};
+
+//! \brief Additional Crashpad-specific information carried within a minidump
+//! file.
+//!
+//! This structure is versioned. When changing this structure, leave the
+//! existing structure intact so that earlier parsers will be able to understand
+//! the fields they are aware of, and make additions at the end of the
+//! structure. Revise #kVersion and document each field’s validity based on
+//! #version, so that newer parsers will be able to determine whether the added
+//! fields are valid or not.
+struct ALIGNAS(4) PACKED MinidumpCrashpadInfo {
+ // UUID has a constructor, which makes it non-POD, which makes this structure
+ // non-POD. In order for the default constructor to zero-initialize other
+ // members, an explicit constructor must be provided.
+ MinidumpCrashpadInfo()
+ : version(),
+ report_id(),
+ client_id(),
+ simple_annotations(),
+ module_list() {
+ }
+
+ //! \brief The structure’s currently-defined version number.
+ //!
+ //! \sa version
+ static const uint32_t kVersion = 1;
+
+ //! \brief The structure’s version number.
+ //!
+ //! Readers can use this field to determine which other fields in the
+ //! structure are valid. Upon encountering a value greater than #kVersion, a
+ //! reader should assume that the structure’s layout is compatible with the
+ //! structure defined as having value #kVersion.
+ //!
+ //! Writers may produce values less than #kVersion in this field if there is
+ //! no need for any fields present in later versions.
+ uint32_t version;
+
+ //! \brief A %UUID identifying an individual crash report.
+ //!
+ //! This provides a stable identifier for a crash even as the report is
+ //! converted to different formats, provided that all formats support storing
+ //! a crash report ID.
+ //!
+ //! If no identifier is available, this field will contain zeroes.
+ //!
+ //! This field is present when #version is at least `1`.
+ UUID report_id;
+
+ //! \brief A %UUID identifying the client that crashed.
+ //!
+ //! Client identification is within the scope of the application, but it is
+ //! expected that the identifier will be unique for an instance of Crashpad
+ //! monitoring an application or set of applications for a user. The
+ //! identifier shall remain stable over time.
+ //!
+ //! If no identifier is available, this field will contain zeroes.
+ //!
+ //! This field is present when #version is at least `1`.
+ UUID client_id;
+
+ //! \brief A MinidumpSimpleStringDictionary pointing to strings interpreted as
+ //! key-value pairs.
+ //!
+ //! These key-value pairs correspond to
+ //! ProcessSnapshot::AnnotationsSimpleMap().
+ //!
+ //! This field is present when #version is at least `1`.
+ MINIDUMP_LOCATION_DESCRIPTOR simple_annotations;
+
+ //! \brief A pointer to a #MinidumpModuleCrashpadInfoList structure.
+ //!
+ //! This field is present when #version is at least `1`.
+ MINIDUMP_LOCATION_DESCRIPTOR module_list;
+};
+
+#if defined(COMPILER_MSVC)
+#pragma pack(pop)
+#endif // COMPILER_MSVC
+#undef PACKED
+
+MSVC_POP_WARNING(); // C4200
+
+} // namespace crashpad
+
+#endif // CRASHPAD_MINIDUMP_MINIDUMP_EXTENSIONS_H_
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc
new file mode 100644
index 00000000000..3d321c0b335
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc
@@ -0,0 +1,229 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_file_writer.h"
+
+#include "base/logging.h"
+#include "minidump/minidump_crashpad_info_writer.h"
+#include "minidump/minidump_exception_writer.h"
+#include "minidump/minidump_memory_writer.h"
+#include "minidump/minidump_misc_info_writer.h"
+#include "minidump/minidump_module_writer.h"
+#include "minidump/minidump_system_info_writer.h"
+#include "minidump/minidump_thread_id_map.h"
+#include "minidump/minidump_thread_writer.h"
+#include "minidump/minidump_writer_util.h"
+#include "snapshot/process_snapshot.h"
+#include "util/file/file_writer.h"
+#include "util/numeric/safe_assignment.h"
+
+namespace crashpad {
+
+MinidumpFileWriter::MinidumpFileWriter()
+ : MinidumpWritable(), header_(), streams_(), stream_types_() {
+ // Don’t set the signature field right away. Leave it set to 0, so that a
+ // partially-written minidump file isn’t confused for a complete and valid
+ // one. The header will be rewritten in WriteToFile().
+ header_.Signature = 0;
+
+ header_.Version = MINIDUMP_VERSION;
+ header_.CheckSum = 0;
+ header_.Flags = MiniDumpNormal;
+}
+
+MinidumpFileWriter::~MinidumpFileWriter() {
+}
+
+void MinidumpFileWriter::InitializeFromSnapshot(
+ const ProcessSnapshot* process_snapshot) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK_EQ(header_.Signature, 0u);
+ DCHECK_EQ(header_.TimeDateStamp, 0u);
+ DCHECK_EQ(header_.Flags, MiniDumpNormal);
+ DCHECK(streams_.empty());
+
+ // This time is truncated to an integer number of seconds, not rounded, for
+ // compatibility with the truncation of process_snapshot->ProcessStartTime()
+ // done by MinidumpMiscInfoWriter::InitializeFromSnapshot(). Handling both
+ // timestamps in the same way allows the highest-fidelity computation of
+ // process uptime as the difference between the two values.
+ timeval snapshot_time;
+ process_snapshot->SnapshotTime(&snapshot_time);
+ SetTimestamp(snapshot_time.tv_sec);
+
+ const SystemSnapshot* system_snapshot = process_snapshot->System();
+ auto system_info = make_scoped_ptr(new MinidumpSystemInfoWriter());
+ system_info->InitializeFromSnapshot(system_snapshot);
+ AddStream(system_info.Pass());
+
+ auto misc_info = make_scoped_ptr(new MinidumpMiscInfoWriter());
+ misc_info->InitializeFromSnapshot(process_snapshot);
+ AddStream(misc_info.Pass());
+
+ auto memory_list = make_scoped_ptr(new MinidumpMemoryListWriter());
+ auto thread_list = make_scoped_ptr(new MinidumpThreadListWriter());
+ thread_list->SetMemoryListWriter(memory_list.get());
+ MinidumpThreadIDMap thread_id_map;
+ thread_list->InitializeFromSnapshot(process_snapshot->Threads(),
+ &thread_id_map);
+ AddStream(thread_list.Pass());
+
+ const ExceptionSnapshot* exception_snapshot = process_snapshot->Exception();
+ if (exception_snapshot) {
+ auto exception = make_scoped_ptr(new MinidumpExceptionWriter());
+ exception->InitializeFromSnapshot(exception_snapshot, thread_id_map);
+ AddStream(exception.Pass());
+ }
+
+ auto module_list = make_scoped_ptr(new MinidumpModuleListWriter());
+ module_list->InitializeFromSnapshot(process_snapshot->Modules());
+ AddStream(module_list.Pass());
+
+ auto crashpad_info = make_scoped_ptr(new MinidumpCrashpadInfoWriter());
+ crashpad_info->InitializeFromSnapshot(process_snapshot);
+
+ // Since the MinidumpCrashpadInfo stream is an extension, it’s safe to not add
+ // it to the minidump file if it wouldn’t carry any useful information.
+ if (crashpad_info->IsUseful()) {
+ AddStream(crashpad_info.Pass());
+ }
+
+ AddStream(memory_list.Pass());
+}
+
+void MinidumpFileWriter::SetTimestamp(time_t timestamp) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ internal::MinidumpWriterUtil::AssignTimeT(&header_.TimeDateStamp, timestamp);
+}
+
+void MinidumpFileWriter::AddStream(
+ scoped_ptr<internal::MinidumpStreamWriter> stream) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ MinidumpStreamType stream_type = stream->StreamType();
+
+ auto rv = stream_types_.insert(stream_type);
+ CHECK(rv.second) << "stream_type " << stream_type << " already present";
+
+ streams_.push_back(stream.release());
+
+ DCHECK_EQ(streams_.size(), stream_types_.size());
+}
+
+bool MinidumpFileWriter::WriteEverything(FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ FileOffset start_offset = file_writer->Seek(0, SEEK_CUR);
+ if (start_offset < 0) {
+ return false;
+ }
+
+ if (!MinidumpWritable::WriteEverything(file_writer)) {
+ return false;
+ }
+
+ FileOffset end_offset = file_writer->Seek(0, SEEK_CUR);
+ if (end_offset < 0) {
+ return false;
+ }
+
+ // Now that the entire minidump file has been completely written, go back to
+ // the beginning and rewrite the header with the correct signature to identify
+ // it as a valid minidump file.
+ header_.Signature = MINIDUMP_SIGNATURE;
+
+ if (file_writer->Seek(start_offset, SEEK_SET) != 0) {
+ return false;
+ }
+
+ if (!file_writer->Write(&header_, sizeof(header_))) {
+ return false;
+ }
+
+ // Seek back to the end of the file, in case some non-minidump content will be
+ // written to the file after the minidump content.
+ return file_writer->Seek(end_offset, SEEK_SET);
+}
+
+bool MinidumpFileWriter::Freeze() {
+ DCHECK_EQ(state(), kStateMutable);
+
+ if (!MinidumpWritable::Freeze()) {
+ return false;
+ }
+
+ size_t stream_count = streams_.size();
+ CHECK_EQ(stream_count, stream_types_.size());
+
+ if (!AssignIfInRange(&header_.NumberOfStreams, stream_count)) {
+ LOG(ERROR) << "stream_count " << stream_count << " out of range";
+ return false;
+ }
+
+ return true;
+}
+
+size_t MinidumpFileWriter::SizeOfObject() {
+ DCHECK_GE(state(), kStateFrozen);
+ DCHECK_EQ(streams_.size(), stream_types_.size());
+
+ return sizeof(header_) + streams_.size() * sizeof(MINIDUMP_DIRECTORY);
+}
+
+std::vector<internal::MinidumpWritable*> MinidumpFileWriter::Children() {
+ DCHECK_GE(state(), kStateFrozen);
+ DCHECK_EQ(streams_.size(), stream_types_.size());
+
+ std::vector<MinidumpWritable*> children;
+ for (internal::MinidumpStreamWriter* stream : streams_) {
+ children.push_back(stream);
+ }
+
+ return children;
+}
+
+bool MinidumpFileWriter::WillWriteAtOffsetImpl(FileOffset offset) {
+ DCHECK_EQ(state(), kStateFrozen);
+ DCHECK_EQ(offset, 0);
+ DCHECK_EQ(streams_.size(), stream_types_.size());
+
+ auto directory_offset = streams_.empty() ? 0 : offset + sizeof(header_);
+ if (!AssignIfInRange(&header_.StreamDirectoryRva, directory_offset)) {
+ LOG(ERROR) << "offset " << directory_offset << " out of range";
+ return false;
+ }
+
+ return MinidumpWritable::WillWriteAtOffsetImpl(offset);
+}
+
+bool MinidumpFileWriter::WriteObject(FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateWritable);
+ DCHECK_EQ(streams_.size(), stream_types_.size());
+
+ WritableIoVec iov;
+ iov.iov_base = &header_;
+ iov.iov_len = sizeof(header_);
+ std::vector<WritableIoVec> iovecs(1, iov);
+
+ for (internal::MinidumpStreamWriter* stream : streams_) {
+ iov.iov_base = stream->DirectoryListEntry();
+ iov.iov_len = sizeof(MINIDUMP_DIRECTORY);
+ iovecs.push_back(iov);
+ }
+
+ return file_writer->WriteIoVec(&iovecs);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_file_writer.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_file_writer.h
new file mode 100644
index 00000000000..8b738b64900
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_file_writer.h
@@ -0,0 +1,124 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_MINIDUMP_MINIDUMP_FILE_WRITER_H_
+#define CRASHPAD_MINIDUMP_MINIDUMP_FILE_WRITER_H_
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <sys/types.h>
+
+#include <set>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "minidump/minidump_extensions.h"
+#include "minidump/minidump_stream_writer.h"
+#include "minidump/minidump_writable.h"
+#include "util/file/file_io.h"
+#include "util/stdlib/pointer_container.h"
+
+namespace crashpad {
+
+class ProcessSnapshot;
+
+//! \brief The root-level object in a minidump file.
+//!
+//! This object writes a MINIDUMP_HEADER and list of MINIDUMP_DIRECTORY entries
+//! to a minidump file.
+class MinidumpFileWriter final : public internal::MinidumpWritable {
+ public:
+ MinidumpFileWriter();
+ ~MinidumpFileWriter() override;
+
+ //! \brief Initializes the MinidumpFileWriter and populates it with
+ //! appropriate child streams based on \a process_snapshot.
+ //!
+ //! This method will add additional streams to the minidump file as children
+ //! of the MinidumpFileWriter object and as pointees of the top-level
+ //! MINIDUMP_DIRECTORY. To do so, it will obtain other snapshot information
+ //! from \a process_snapshot, such as a SystemSnapshot, lists of
+ //! ThreadSnapshot and ModuleSnapshot objects, and, if available, an
+ //! ExceptionSnapshot.
+ //!
+ //! The streams are added in the order that they are expected to be most
+ //! useful to minidump readers, to improve data locality and minimize seeking.
+ //! The streams are added in this order:
+ //! - kMinidumpStreamTypeSystemInfo
+ //! - kMinidumpStreamTypeMiscInfo
+ //! - kMinidumpStreamTypeThreadList
+ //! - kMinidumpStreamTypeException (if present)
+ //! - kMinidumpStreamTypeModuleList
+ //! - kMinidumpStreamTypeCrashpadInfo (if present)
+ //! - kMinidumpStreamTypeMemoryList
+ //!
+ //! \param[in] process_snapshot The process snapshot to use as source data.
+ //!
+ //! \note Valid in #kStateMutable. No mutator methods may be called before
+ //! this method, and it is not normally necessary to call any mutator
+ //! methods after this method.
+ void InitializeFromSnapshot(const ProcessSnapshot* process_snapshot);
+
+ //! \brief Sets MINIDUMP_HEADER::Timestamp.
+ //!
+ //! \note Valid in #kStateMutable.
+ void SetTimestamp(time_t timestamp);
+
+ //! \brief Adds a stream to the minidump file and arranges for a
+ //! MINIDUMP_DIRECTORY entry to point to it.
+ //!
+ //! This object takes ownership of \a stream and becomes its parent in the
+ //! overall tree of internal::MinidumpWritable objects.
+ //!
+ //! At most one object of each stream type (as obtained from
+ //! internal::MinidumpStreamWriter::StreamType()) may be added to a
+ //! MinidumpFileWriter object. It is an error to attempt to add multiple
+ //! streams with the same stream type.
+ //!
+ //! \note Valid in #kStateMutable.
+ void AddStream(scoped_ptr<internal::MinidumpStreamWriter> stream);
+
+ // MinidumpWritable:
+
+ //! \copydoc internal::MinidumpWritable::WriteEverything()
+ //!
+ //! This method does not initially write the final value for
+ //! MINIDUMP_HEADER::Signature. After all child objects have been written, it
+ //! rewinds to the beginning of the file and writes the correct value for this
+ //! field. This prevents incompletely-written minidump files from being
+ //! mistaken for valid ones.
+ bool WriteEverything(FileWriterInterface* file_writer) override;
+
+ protected:
+ // MinidumpWritable:
+ bool Freeze() override;
+ size_t SizeOfObject() override;
+ std::vector<MinidumpWritable*> Children() override;
+ bool WillWriteAtOffsetImpl(FileOffset offset) override;
+ bool WriteObject(FileWriterInterface* file_writer) override;
+
+ private:
+ MINIDUMP_HEADER header_;
+ PointerVector<internal::MinidumpStreamWriter> streams_;
+
+ // Protects against multiple streams with the same ID being added.
+ std::set<MinidumpStreamType> stream_types_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpFileWriter);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_MINIDUMP_MINIDUMP_WRITER_H_
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_file_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_file_writer_test.cc
new file mode 100644
index 00000000000..cc6b1ce57ee
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_file_writer_test.cc
@@ -0,0 +1,445 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_file_writer.h"
+
+#include <windows.h>
+#include <dbghelp.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "gtest/gtest.h"
+#include "minidump/minidump_stream_writer.h"
+#include "minidump/minidump_writable.h"
+#include "minidump/test/minidump_file_writer_test_util.h"
+#include "minidump/test/minidump_writable_test_util.h"
+#include "snapshot/test/test_cpu_context.h"
+#include "snapshot/test/test_exception_snapshot.h"
+#include "snapshot/test/test_module_snapshot.h"
+#include "snapshot/test/test_process_snapshot.h"
+#include "snapshot/test/test_system_snapshot.h"
+#include "snapshot/test/test_thread_snapshot.h"
+#include "test/gtest_death_check.h"
+#include "util/file/string_file.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(MinidumpFileWriter, Empty) {
+ MinidumpFileWriter minidump_file;
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file.WriteEverything(&string_file));
+ ASSERT_EQ(sizeof(MINIDUMP_HEADER), string_file.string().size());
+
+ const MINIDUMP_DIRECTORY* directory;
+ const MINIDUMP_HEADER* header =
+ MinidumpHeaderAtStart(string_file.string(), &directory);
+ ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 0, 0));
+ EXPECT_FALSE(directory);
+}
+
+class TestStream final : public internal::MinidumpStreamWriter {
+ public:
+ TestStream(MinidumpStreamType stream_type,
+ size_t stream_size,
+ uint8_t stream_value)
+ : stream_data_(stream_size, stream_value), stream_type_(stream_type) {}
+
+ ~TestStream() override {}
+
+ // MinidumpStreamWriter:
+ MinidumpStreamType StreamType() const override {
+ return stream_type_;
+ }
+
+ protected:
+ // MinidumpWritable:
+ size_t SizeOfObject() override {
+ EXPECT_GE(state(), kStateFrozen);
+ return stream_data_.size();
+ }
+
+ bool WriteObject(FileWriterInterface* file_writer) override {
+ EXPECT_EQ(state(), kStateWritable);
+ return file_writer->Write(&stream_data_[0], stream_data_.size());
+ }
+
+ private:
+ std::string stream_data_;
+ MinidumpStreamType stream_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestStream);
+};
+
+TEST(MinidumpFileWriter, OneStream) {
+ MinidumpFileWriter minidump_file;
+ const time_t kTimestamp = 0x155d2fb8;
+ minidump_file.SetTimestamp(kTimestamp);
+
+ const size_t kStreamSize = 5;
+ const MinidumpStreamType kStreamType = static_cast<MinidumpStreamType>(0x4d);
+ const uint8_t kStreamValue = 0x5a;
+ auto stream =
+ make_scoped_ptr(new TestStream(kStreamType, kStreamSize, kStreamValue));
+ minidump_file.AddStream(stream.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file.WriteEverything(&string_file));
+
+ const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
+ const size_t kStreamOffset = kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);
+ const size_t kFileSize = kStreamOffset + kStreamSize;
+
+ ASSERT_EQ(kFileSize, string_file.string().size());
+
+ const MINIDUMP_DIRECTORY* directory;
+ const MINIDUMP_HEADER* header =
+ MinidumpHeaderAtStart(string_file.string(), &directory);
+ ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, kTimestamp));
+ ASSERT_TRUE(directory);
+
+ EXPECT_EQ(kStreamType, directory[0].StreamType);
+ EXPECT_EQ(kStreamSize, directory[0].Location.DataSize);
+ EXPECT_EQ(kStreamOffset, directory[0].Location.Rva);
+
+ const uint8_t* stream_data = MinidumpWritableAtLocationDescriptor<uint8_t>(
+ string_file.string(), directory[0].Location);
+ ASSERT_TRUE(stream_data);
+
+ std::string expected_stream(kStreamSize, kStreamValue);
+ EXPECT_EQ(0, memcmp(stream_data, expected_stream.c_str(), kStreamSize));
+}
+
+TEST(MinidumpFileWriter, ThreeStreams) {
+ MinidumpFileWriter minidump_file;
+ const time_t kTimestamp = 0x155d2fb8;
+ minidump_file.SetTimestamp(kTimestamp);
+
+ const size_t kStream0Size = 5;
+ const MinidumpStreamType kStream0Type = static_cast<MinidumpStreamType>(0x6d);
+ const uint8_t kStream0Value = 0x5a;
+ auto stream0 = make_scoped_ptr(
+ new TestStream(kStream0Type, kStream0Size, kStream0Value));
+ minidump_file.AddStream(stream0.Pass());
+
+ // Make the second stream’s type be a smaller quantity than the first stream’s
+ // to test that the streams show up in the order that they were added, not in
+ // numeric order.
+ const size_t kStream1Size = 3;
+ const MinidumpStreamType kStream1Type = static_cast<MinidumpStreamType>(0x4d);
+ const uint8_t kStream1Value = 0xa5;
+ auto stream1 = make_scoped_ptr(
+ new TestStream(kStream1Type, kStream1Size, kStream1Value));
+ minidump_file.AddStream(stream1.Pass());
+
+ const size_t kStream2Size = 1;
+ const MinidumpStreamType kStream2Type = static_cast<MinidumpStreamType>(0x7e);
+ const uint8_t kStream2Value = 0x36;
+ auto stream2 = make_scoped_ptr(
+ new TestStream(kStream2Type, kStream2Size, kStream2Value));
+ minidump_file.AddStream(stream2.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file.WriteEverything(&string_file));
+
+ const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
+ const size_t kStream0Offset =
+ kDirectoryOffset + 3 * sizeof(MINIDUMP_DIRECTORY);
+ const size_t kStream1Padding = 3;
+ const size_t kStream1Offset = kStream0Offset + kStream0Size + kStream1Padding;
+ const size_t kStream2Padding = 1;
+ const size_t kStream2Offset = kStream1Offset + kStream1Size + kStream2Padding;
+ const size_t kFileSize = kStream2Offset + kStream2Size;
+
+ ASSERT_EQ(kFileSize, string_file.string().size());
+
+ const MINIDUMP_DIRECTORY* directory;
+ const MINIDUMP_HEADER* header =
+ MinidumpHeaderAtStart(string_file.string(), &directory);
+ ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 3, kTimestamp));
+ ASSERT_TRUE(directory);
+
+ EXPECT_EQ(kStream0Type, directory[0].StreamType);
+ EXPECT_EQ(kStream0Size, directory[0].Location.DataSize);
+ EXPECT_EQ(kStream0Offset, directory[0].Location.Rva);
+ EXPECT_EQ(kStream1Type, directory[1].StreamType);
+ EXPECT_EQ(kStream1Size, directory[1].Location.DataSize);
+ EXPECT_EQ(kStream1Offset, directory[1].Location.Rva);
+ EXPECT_EQ(kStream2Type, directory[2].StreamType);
+ EXPECT_EQ(kStream2Size, directory[2].Location.DataSize);
+ EXPECT_EQ(kStream2Offset, directory[2].Location.Rva);
+
+ const uint8_t* stream0_data = MinidumpWritableAtLocationDescriptor<uint8_t>(
+ string_file.string(), directory[0].Location);
+ ASSERT_TRUE(stream0_data);
+
+ std::string expected_stream0(kStream0Size, kStream0Value);
+ EXPECT_EQ(0, memcmp(stream0_data, expected_stream0.c_str(), kStream0Size));
+
+ const int kZeroes[16] = {};
+ ASSERT_GE(sizeof(kZeroes), kStream1Padding);
+ EXPECT_EQ(0, memcmp(stream0_data + kStream0Size, kZeroes, kStream1Padding));
+
+ const uint8_t* stream1_data = MinidumpWritableAtLocationDescriptor<uint8_t>(
+ string_file.string(), directory[1].Location);
+ ASSERT_TRUE(stream1_data);
+
+ std::string expected_stream1(kStream1Size, kStream1Value);
+ EXPECT_EQ(0, memcmp(stream1_data, expected_stream1.c_str(), kStream1Size));
+
+ ASSERT_GE(sizeof(kZeroes), kStream2Padding);
+ EXPECT_EQ(0, memcmp(stream1_data + kStream1Size, kZeroes, kStream2Padding));
+
+ const uint8_t* stream2_data = MinidumpWritableAtLocationDescriptor<uint8_t>(
+ string_file.string(), directory[2].Location);
+ ASSERT_TRUE(stream2_data);
+
+ std::string expected_stream2(kStream2Size, kStream2Value);
+ EXPECT_EQ(0, memcmp(stream2_data, expected_stream2.c_str(), kStream2Size));
+}
+
+TEST(MinidumpFileWriter, ZeroLengthStream) {
+ MinidumpFileWriter minidump_file;
+
+ const size_t kStreamSize = 0;
+ const MinidumpStreamType kStreamType = static_cast<MinidumpStreamType>(0x4d);
+ auto stream = make_scoped_ptr(new TestStream(kStreamType, kStreamSize, 0));
+ minidump_file.AddStream(stream.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file.WriteEverything(&string_file));
+
+ const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
+ const size_t kStreamOffset = kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);
+ const size_t kFileSize = kStreamOffset + kStreamSize;
+
+ ASSERT_EQ(kFileSize, string_file.string().size());
+
+ const MINIDUMP_DIRECTORY* directory;
+ const MINIDUMP_HEADER* header =
+ MinidumpHeaderAtStart(string_file.string(), &directory);
+ ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));
+ ASSERT_TRUE(directory);
+
+ EXPECT_EQ(kStreamType, directory[0].StreamType);
+ EXPECT_EQ(kStreamSize, directory[0].Location.DataSize);
+ EXPECT_EQ(kStreamOffset, directory[0].Location.Rva);
+}
+
+TEST(MinidumpFileWriter, InitializeFromSnapshot_Basic) {
+ const uint32_t kSnapshotTime = 0x4976043c;
+ const timeval kSnapshotTimeval = { static_cast<time_t>(kSnapshotTime), 0 };
+
+ TestProcessSnapshot process_snapshot;
+ process_snapshot.SetSnapshotTime(kSnapshotTimeval);
+
+ auto system_snapshot = make_scoped_ptr(new TestSystemSnapshot());
+ system_snapshot->SetCPUArchitecture(kCPUArchitectureX86_64);
+ system_snapshot->SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX);
+ process_snapshot.SetSystem(system_snapshot.Pass());
+
+ MinidumpFileWriter minidump_file_writer;
+ minidump_file_writer.InitializeFromSnapshot(&process_snapshot);
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_DIRECTORY* directory;
+ const MINIDUMP_HEADER* header =
+ MinidumpHeaderAtStart(string_file.string(), &directory);
+ ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 5, kSnapshotTime));
+ ASSERT_TRUE(directory);
+
+ EXPECT_EQ(kMinidumpStreamTypeSystemInfo, directory[0].StreamType);
+ EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_SYSTEM_INFO>(
+ string_file.string(), directory[0].Location));
+
+ EXPECT_EQ(kMinidumpStreamTypeMiscInfo, directory[1].StreamType);
+ EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MISC_INFO_4>(
+ string_file.string(), directory[1].Location));
+
+ EXPECT_EQ(kMinidumpStreamTypeThreadList, directory[2].StreamType);
+ EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_LIST>(
+ string_file.string(), directory[2].Location));
+
+ EXPECT_EQ(kMinidumpStreamTypeModuleList, directory[3].StreamType);
+ EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>(
+ string_file.string(), directory[3].Location));
+
+ EXPECT_EQ(kMinidumpStreamTypeMemoryList, directory[4].StreamType);
+ EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>(
+ string_file.string(), directory[4].Location));
+}
+
+TEST(MinidumpFileWriter, InitializeFromSnapshot_Exception) {
+ // In a 32-bit environment, this will give a “timestamp out of range” warning,
+ // but the test should complete without failure.
+ const uint32_t kSnapshotTime = 0xfd469ab8;
+ MSVC_SUPPRESS_WARNING(4309); // Truncation of constant value.
+ const timeval kSnapshotTimeval = { static_cast<time_t>(kSnapshotTime), 0 };
+
+ TestProcessSnapshot process_snapshot;
+ process_snapshot.SetSnapshotTime(kSnapshotTimeval);
+
+ auto system_snapshot = make_scoped_ptr(new TestSystemSnapshot());
+ system_snapshot->SetCPUArchitecture(kCPUArchitectureX86_64);
+ system_snapshot->SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX);
+ process_snapshot.SetSystem(system_snapshot.Pass());
+
+ auto thread_snapshot = make_scoped_ptr(new TestThreadSnapshot());
+ InitializeCPUContextX86_64(thread_snapshot->MutableContext(), 5);
+ process_snapshot.AddThread(thread_snapshot.Pass());
+
+ auto exception_snapshot = make_scoped_ptr(new TestExceptionSnapshot());
+ InitializeCPUContextX86_64(exception_snapshot->MutableContext(), 11);
+ process_snapshot.SetException(exception_snapshot.Pass());
+
+ // The module does not have anything that needs to be represented in a
+ // MinidumpModuleCrashpadInfo structure, so no such structure is expected to
+ // be present, which will in turn suppress the addition of a
+ // MinidumpCrashpadInfo stream.
+ auto module_snapshot = make_scoped_ptr(new TestModuleSnapshot());
+ process_snapshot.AddModule(module_snapshot.Pass());
+
+ MinidumpFileWriter minidump_file_writer;
+ minidump_file_writer.InitializeFromSnapshot(&process_snapshot);
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_DIRECTORY* directory;
+ const MINIDUMP_HEADER* header =
+ MinidumpHeaderAtStart(string_file.string(), &directory);
+ ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 6, kSnapshotTime));
+ ASSERT_TRUE(directory);
+
+ EXPECT_EQ(kMinidumpStreamTypeSystemInfo, directory[0].StreamType);
+ EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_SYSTEM_INFO>(
+ string_file.string(), directory[0].Location));
+
+ EXPECT_EQ(kMinidumpStreamTypeMiscInfo, directory[1].StreamType);
+ EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MISC_INFO_4>(
+ string_file.string(), directory[1].Location));
+
+ EXPECT_EQ(kMinidumpStreamTypeThreadList, directory[2].StreamType);
+ EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_LIST>(
+ string_file.string(), directory[2].Location));
+
+ EXPECT_EQ(kMinidumpStreamTypeException, directory[3].StreamType);
+ EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_EXCEPTION_STREAM>(
+ string_file.string(), directory[3].Location));
+
+ EXPECT_EQ(kMinidumpStreamTypeModuleList, directory[4].StreamType);
+ EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>(
+ string_file.string(), directory[4].Location));
+
+ EXPECT_EQ(kMinidumpStreamTypeMemoryList, directory[5].StreamType);
+ EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>(
+ string_file.string(), directory[5].Location));
+}
+
+TEST(MinidumpFileWriter, InitializeFromSnapshot_CrashpadInfo) {
+ const uint32_t kSnapshotTime = 0x15393bd3;
+ const timeval kSnapshotTimeval = { static_cast<time_t>(kSnapshotTime), 0 };
+
+ TestProcessSnapshot process_snapshot;
+ process_snapshot.SetSnapshotTime(kSnapshotTimeval);
+
+ auto system_snapshot = make_scoped_ptr(new TestSystemSnapshot());
+ system_snapshot->SetCPUArchitecture(kCPUArchitectureX86_64);
+ system_snapshot->SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX);
+ process_snapshot.SetSystem(system_snapshot.Pass());
+
+ auto thread_snapshot = make_scoped_ptr(new TestThreadSnapshot());
+ InitializeCPUContextX86_64(thread_snapshot->MutableContext(), 5);
+ process_snapshot.AddThread(thread_snapshot.Pass());
+
+ auto exception_snapshot = make_scoped_ptr(new TestExceptionSnapshot());
+ InitializeCPUContextX86_64(exception_snapshot->MutableContext(), 11);
+ process_snapshot.SetException(exception_snapshot.Pass());
+
+ // The module needs an annotation for the MinidumpCrashpadInfo stream to be
+ // considered useful and be included.
+ auto module_snapshot = make_scoped_ptr(new TestModuleSnapshot());
+ std::vector<std::string> annotations_list(1, std::string("annotation"));
+ module_snapshot->SetAnnotationsVector(annotations_list);
+ process_snapshot.AddModule(module_snapshot.Pass());
+
+ MinidumpFileWriter minidump_file_writer;
+ minidump_file_writer.InitializeFromSnapshot(&process_snapshot);
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_DIRECTORY* directory;
+ const MINIDUMP_HEADER* header =
+ MinidumpHeaderAtStart(string_file.string(), &directory);
+ ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 7, kSnapshotTime));
+ ASSERT_TRUE(directory);
+
+ EXPECT_EQ(kMinidumpStreamTypeSystemInfo, directory[0].StreamType);
+ EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_SYSTEM_INFO>(
+ string_file.string(), directory[0].Location));
+
+ EXPECT_EQ(kMinidumpStreamTypeMiscInfo, directory[1].StreamType);
+ EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MISC_INFO_4>(
+ string_file.string(), directory[1].Location));
+
+ EXPECT_EQ(kMinidumpStreamTypeThreadList, directory[2].StreamType);
+ EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_LIST>(
+ string_file.string(), directory[2].Location));
+
+ EXPECT_EQ(kMinidumpStreamTypeException, directory[3].StreamType);
+ EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_EXCEPTION_STREAM>(
+ string_file.string(), directory[3].Location));
+
+ EXPECT_EQ(kMinidumpStreamTypeModuleList, directory[4].StreamType);
+ EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>(
+ string_file.string(), directory[4].Location));
+
+ EXPECT_EQ(kMinidumpStreamTypeCrashpadInfo, directory[5].StreamType);
+ EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MinidumpCrashpadInfo>(
+ string_file.string(), directory[5].Location));
+
+ EXPECT_EQ(kMinidumpStreamTypeMemoryList, directory[6].StreamType);
+ EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>(
+ string_file.string(), directory[6].Location));
+}
+
+TEST(MinidumpFileWriterDeathTest, SameStreamType) {
+ MinidumpFileWriter minidump_file;
+
+ const size_t kStream0Size = 5;
+ const MinidumpStreamType kStream0Type = static_cast<MinidumpStreamType>(0x4d);
+ const uint8_t kStream0Value = 0x5a;
+ auto stream0 = make_scoped_ptr(
+ new TestStream(kStream0Type, kStream0Size, kStream0Value));
+ minidump_file.AddStream(stream0.Pass());
+
+ // It is an error to add a second stream of the same type.
+ const size_t kStream1Size = 3;
+ const MinidumpStreamType kStream1Type = static_cast<MinidumpStreamType>(0x4d);
+ const uint8_t kStream1Value = 0xa5;
+ auto stream1 = make_scoped_ptr(
+ new TestStream(kStream1Type, kStream1Size, kStream1Value));
+ ASSERT_DEATH_CHECK(minidump_file.AddStream(stream1.Pass()),
+ "already present");
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.cc
new file mode 100644
index 00000000000..606623e453e
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.cc
@@ -0,0 +1,260 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_memory_writer.h"
+
+#include "base/auto_reset.h"
+#include "base/logging.h"
+#include "snapshot/memory_snapshot.h"
+#include "util/file/file_writer.h"
+#include "util/numeric/safe_assignment.h"
+
+namespace crashpad {
+namespace {
+
+class SnapshotMinidumpMemoryWriter final : public MinidumpMemoryWriter,
+ public MemorySnapshot::Delegate {
+ public:
+ explicit SnapshotMinidumpMemoryWriter(const MemorySnapshot* memory_snapshot)
+ : MinidumpMemoryWriter(),
+ MemorySnapshot::Delegate(),
+ memory_snapshot_(memory_snapshot),
+ file_writer_(nullptr) {
+ }
+
+ ~SnapshotMinidumpMemoryWriter() override {}
+
+ // MemorySnapshot::Delegate:
+
+ bool MemorySnapshotDelegateRead(void* data, size_t size) override {
+ DCHECK_EQ(state(), kStateWritable);
+ DCHECK_EQ(size, MemoryRangeSize());
+ return file_writer_->Write(data, size);
+ }
+
+ protected:
+ // MinidumpMemoryWriter:
+
+ bool WriteObject(FileWriterInterface* file_writer) override {
+ DCHECK_EQ(state(), kStateWritable);
+ DCHECK(!file_writer_);
+
+ base::AutoReset<FileWriterInterface*> file_writer_reset(&file_writer_,
+ file_writer);
+
+ // This will result in MemorySnapshotDelegateRead() being called.
+ return memory_snapshot_->Read(this);
+ }
+
+ uint64_t MemoryRangeBaseAddress() const override {
+ DCHECK_EQ(state(), kStateFrozen);
+ return memory_snapshot_->Address();
+ }
+
+ size_t MemoryRangeSize() const override {
+ DCHECK_GE(state(), kStateFrozen);
+ return memory_snapshot_->Size();
+ }
+
+ private:
+ const MemorySnapshot* memory_snapshot_;
+ FileWriterInterface* file_writer_;
+
+ DISALLOW_COPY_AND_ASSIGN(SnapshotMinidumpMemoryWriter);
+};
+
+} // namespace
+
+MinidumpMemoryWriter::~MinidumpMemoryWriter() {
+}
+
+scoped_ptr<MinidumpMemoryWriter> MinidumpMemoryWriter::CreateFromSnapshot(
+ const MemorySnapshot* memory_snapshot) {
+ return make_scoped_ptr(new SnapshotMinidumpMemoryWriter(memory_snapshot));
+}
+
+const MINIDUMP_MEMORY_DESCRIPTOR*
+MinidumpMemoryWriter::MinidumpMemoryDescriptor() const {
+ DCHECK_EQ(state(), kStateWritable);
+
+ return &memory_descriptor_;
+}
+
+void MinidumpMemoryWriter::RegisterMemoryDescriptor(
+ MINIDUMP_MEMORY_DESCRIPTOR* memory_descriptor) {
+ DCHECK_LE(state(), kStateFrozen);
+
+ registered_memory_descriptors_.push_back(memory_descriptor);
+ RegisterLocationDescriptor(&memory_descriptor->Memory);
+}
+
+MinidumpMemoryWriter::MinidumpMemoryWriter()
+ : MinidumpWritable(),
+ memory_descriptor_(),
+ registered_memory_descriptors_() {
+}
+
+bool MinidumpMemoryWriter::Freeze() {
+ DCHECK_EQ(state(), kStateMutable);
+
+ if (!MinidumpWritable::Freeze()) {
+ return false;
+ }
+
+ RegisterMemoryDescriptor(&memory_descriptor_);
+
+ return true;
+}
+
+size_t MinidumpMemoryWriter::Alignment() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ return 16;
+}
+
+size_t MinidumpMemoryWriter::SizeOfObject() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ return MemoryRangeSize();
+}
+
+bool MinidumpMemoryWriter::WillWriteAtOffsetImpl(FileOffset offset) {
+ DCHECK_EQ(state(), kStateFrozen);
+
+ // There will always be at least one registered descriptor, the one for this
+ // object’s own memory_descriptor_ field.
+ DCHECK_GE(registered_memory_descriptors_.size(), 1u);
+
+ uint64_t base_address = MemoryRangeBaseAddress();
+ decltype(registered_memory_descriptors_[0]->StartOfMemoryRange) local_address;
+ if (!AssignIfInRange(&local_address, base_address)) {
+ LOG(ERROR) << "base_address " << base_address << " out of range";
+ return false;
+ }
+
+ for (MINIDUMP_MEMORY_DESCRIPTOR* memory_descriptor :
+ registered_memory_descriptors_) {
+ memory_descriptor->StartOfMemoryRange = local_address;
+ }
+
+ return MinidumpWritable::WillWriteAtOffsetImpl(offset);
+}
+
+internal::MinidumpWritable::Phase MinidumpMemoryWriter::WritePhase() {
+ // Memory dumps are large and are unlikely to be consumed in their entirety.
+ // Data accesses are expected to be sparse and sporadic, and are expected to
+ // occur after all of the other structural and informational data from the
+ // minidump file has been read. Put memory dumps at the end of the minidump
+ // file to improve spatial locality.
+ return kPhaseLate;
+}
+
+MinidumpMemoryListWriter::MinidumpMemoryListWriter()
+ : MinidumpStreamWriter(),
+ memory_writers_(),
+ children_(),
+ memory_list_base_() {
+}
+
+MinidumpMemoryListWriter::~MinidumpMemoryListWriter() {
+}
+
+void MinidumpMemoryListWriter::AddFromSnapshot(
+ const std::vector<const MemorySnapshot*>& memory_snapshots) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ for (const MemorySnapshot* memory_snapshot : memory_snapshots) {
+ scoped_ptr<MinidumpMemoryWriter> memory =
+ MinidumpMemoryWriter::CreateFromSnapshot(memory_snapshot);
+ AddMemory(memory.Pass());
+ }
+}
+
+void MinidumpMemoryListWriter::AddMemory(
+ scoped_ptr<MinidumpMemoryWriter> memory_writer) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ AddExtraMemory(memory_writer.get());
+ children_.push_back(memory_writer.release());
+}
+
+void MinidumpMemoryListWriter::AddExtraMemory(
+ MinidumpMemoryWriter* memory_writer) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ memory_writers_.push_back(memory_writer);
+}
+
+bool MinidumpMemoryListWriter::Freeze() {
+ DCHECK_EQ(state(), kStateMutable);
+
+ if (!MinidumpStreamWriter::Freeze()) {
+ return false;
+ }
+
+ size_t memory_region_count = memory_writers_.size();
+ CHECK_LE(children_.size(), memory_region_count);
+
+ if (!AssignIfInRange(&memory_list_base_.NumberOfMemoryRanges,
+ memory_region_count)) {
+ LOG(ERROR) << "memory_region_count " << memory_region_count
+ << " out of range";
+ return false;
+ }
+
+ return true;
+}
+
+size_t MinidumpMemoryListWriter::SizeOfObject() {
+ DCHECK_GE(state(), kStateFrozen);
+ DCHECK_LE(children_.size(), memory_writers_.size());
+
+ return sizeof(memory_list_base_) +
+ memory_writers_.size() * sizeof(MINIDUMP_MEMORY_DESCRIPTOR);
+}
+
+std::vector<internal::MinidumpWritable*> MinidumpMemoryListWriter::Children() {
+ DCHECK_GE(state(), kStateFrozen);
+ DCHECK_LE(children_.size(), memory_writers_.size());
+
+ std::vector<MinidumpWritable*> children;
+ for (MinidumpMemoryWriter* child : children_) {
+ children.push_back(child);
+ }
+
+ return children;
+}
+
+bool MinidumpMemoryListWriter::WriteObject(FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateWritable);
+
+ WritableIoVec iov;
+ iov.iov_base = &memory_list_base_;
+ iov.iov_len = sizeof(memory_list_base_);
+ std::vector<WritableIoVec> iovecs(1, iov);
+
+ for (const MinidumpMemoryWriter* memory_writer : memory_writers_) {
+ iov.iov_base = memory_writer->MinidumpMemoryDescriptor();
+ iov.iov_len = sizeof(MINIDUMP_MEMORY_DESCRIPTOR);
+ iovecs.push_back(iov);
+ }
+
+ return file_writer->WriteIoVec(&iovecs);
+}
+
+MinidumpStreamType MinidumpMemoryListWriter::StreamType() const {
+ return kMinidumpStreamTypeMemoryList;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.h
new file mode 100644
index 00000000000..6b5f2dfe23b
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.h
@@ -0,0 +1,193 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_WRITER_H_
+#define CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_WRITER_H_
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "minidump/minidump_stream_writer.h"
+#include "minidump/minidump_writable.h"
+#include "util/file/file_io.h"
+#include "util/stdlib/pointer_container.h"
+
+namespace crashpad {
+
+class MemorySnapshot;
+
+//! \brief The base class for writers of memory ranges pointed to by
+//! MINIDUMP_MEMORY_DESCRIPTOR objects in a minidump file.
+//!
+//! This is an abstract base class because users are expected to provide their
+//! own implementations that, when possible, obtain the memory contents
+//! on-demand in their WriteObject() methods. Memory ranges may be large, and
+//! the alternative construction would require the contents of multiple ranges
+//! to be held in memory simultaneously while a minidump file is being written.
+class MinidumpMemoryWriter : public internal::MinidumpWritable {
+ public:
+ ~MinidumpMemoryWriter() override;
+
+ //! \brief Creates a concrete initialized MinidumpMemoryWriter based on \a
+ //! memory_snapshot.
+ //!
+ //! \param[in] memory_snapshot The memory snapshot to use as source data.
+ //!
+ //! \return An object of a MinidumpMemoryWriter subclass initialized using the
+ //! source data in \a memory_snapshot.
+ static scoped_ptr<MinidumpMemoryWriter> CreateFromSnapshot(
+ const MemorySnapshot* memory_snapshot);
+
+ //! \brief Returns a MINIDUMP_MEMORY_DESCRIPTOR referencing the data that this
+ //! object writes.
+ //!
+ //! This method is expected to be called by a MinidumpMemoryListWriter in
+ //! order to obtain a MINIDUMP_MEMORY_DESCRIPTOR to include in its list.
+ //!
+ //! \note Valid in #kStateWritable.
+ const MINIDUMP_MEMORY_DESCRIPTOR* MinidumpMemoryDescriptor() const;
+
+ //! \brief Registers a memory descriptor as one that should point to the
+ //! object on which this method is called.
+ //!
+ //! This method is expected to be called by objects of other classes, when
+ //! those other classes have their own memory descriptors that need to point
+ //! to memory ranges within a minidump file. MinidumpThreadWriter is one such
+ //! class. This method is public for this reason, otherwise it would suffice
+ //! to be private.
+ //!
+ //! \note Valid in #kStateFrozen or any preceding state.
+ void RegisterMemoryDescriptor(MINIDUMP_MEMORY_DESCRIPTOR* memory_descriptor);
+
+ protected:
+ MinidumpMemoryWriter();
+
+ //! \brief Returns the base address of the memory region in the address space
+ //! of the process that the snapshot describes.
+ //!
+ //! \note This method will only be called in #kStateFrozen.
+ virtual uint64_t MemoryRangeBaseAddress() const = 0;
+
+ //! \brief Returns the size of the memory region in bytes.
+ //!
+ //! \note This method will only be called in #kStateFrozen or a subsequent
+ //! state.
+ virtual size_t MemoryRangeSize() const = 0;
+
+ // MinidumpWritable:
+ bool Freeze() override;
+ size_t SizeOfObject() final;
+
+ //! \brief Returns the object’s desired byte-boundary alignment.
+ //!
+ //! Memory regions are aligned to a 16-byte boundary. The actual alignment
+ //! requirements of any data within the memory region are unknown, and may be
+ //! more or less strict than this depending on the platform.
+ //!
+ //! \return `16`.
+ //!
+ //! \note Valid in #kStateFrozen or any subsequent state.
+ size_t Alignment() override;
+
+ bool WillWriteAtOffsetImpl(FileOffset offset) override;
+
+ //! \brief Returns the object’s desired write phase.
+ //!
+ //! Memory regions are written at the end of minidump files, because it is
+ //! expected that unlike most other data in a minidump file, the contents of
+ //! memory regions will be accessed sparsely.
+ //!
+ //! \return #kPhaseLate.
+ //!
+ //! \note Valid in any state.
+ Phase WritePhase() final;
+
+ private:
+ MINIDUMP_MEMORY_DESCRIPTOR memory_descriptor_;
+
+ // weak
+ std::vector<MINIDUMP_MEMORY_DESCRIPTOR*> registered_memory_descriptors_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpMemoryWriter);
+};
+
+//! \brief The writer for a MINIDUMP_MEMORY_LIST stream in a minidump file,
+//! containing a list of MINIDUMP_MEMORY_DESCRIPTOR objects.
+class MinidumpMemoryListWriter final : public internal::MinidumpStreamWriter {
+ public:
+ MinidumpMemoryListWriter();
+ ~MinidumpMemoryListWriter() override;
+
+ //! \brief Adds a concrete initialized MinidumpMemoryWriter for each memory
+ //! snapshot in \a memory_snapshots to the MINIDUMP_MEMORY_LIST.
+ //!
+ //! Memory snapshots are added in the fashion of AddMemory().
+ //!
+ //! \param[in] memory_snapshots The memory snapshots to use as source data.
+ //!
+ //! \note Valid in #kStateMutable.
+ void AddFromSnapshot(
+ const std::vector<const MemorySnapshot*>& memory_snapshots);
+
+ //! \brief Adds a MinidumpMemoryWriter to the MINIDUMP_MEMORY_LIST.
+ //!
+ //! This object takes ownership of \a memory_writer and becomes its parent in
+ //! the overall tree of internal::MinidumpWritable objects.
+ //!
+ //! \note Valid in #kStateMutable.
+ void AddMemory(scoped_ptr<MinidumpMemoryWriter> memory_writer);
+
+ //! \brief Adds a MinidumpMemoryWriter that’s a child of another
+ //! internal::MinidumpWritable object to the MINIDUMP_MEMORY_LIST.
+ //!
+ //! \a memory_writer does not become a child of this object, but the
+ //! MINIDUMP_MEMORY_LIST will still contain a MINIDUMP_MEMORY_DESCRIPTOR for
+ //! it. \a memory_writer must be a child of another object in the
+ //! internal::MinidumpWritable tree.
+ //!
+ //! This method exists to be called by objects that have their own
+ //! MinidumpMemoryWriter children but wish for them to also appear in the
+ //! minidump file’s MINIDUMP_MEMORY_LIST. MinidumpThreadWriter, which has a
+ //! MinidumpMemoryWriter for thread stack memory, is an example.
+ //!
+ //! \note Valid in #kStateMutable.
+ void AddExtraMemory(MinidumpMemoryWriter* memory_writer);
+
+ protected:
+ // MinidumpWritable:
+ bool Freeze() override;
+ size_t SizeOfObject() override;
+ std::vector<MinidumpWritable*> Children() override;
+ bool WriteObject(FileWriterInterface* file_writer) override;
+
+ // MinidumpStreamWriter:
+ MinidumpStreamType StreamType() const override;
+
+ private:
+ std::vector<MinidumpMemoryWriter*> memory_writers_; // weak
+ PointerVector<MinidumpMemoryWriter> children_;
+ MINIDUMP_MEMORY_LIST memory_list_base_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpMemoryListWriter);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_WRITER_H_
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer_test.cc
new file mode 100644
index 00000000000..6292ea0a1f7
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer_test.cc
@@ -0,0 +1,363 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_memory_writer.h"
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <stdint.h>
+
+#include "base/basictypes.h"
+#include "base/format_macros.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+#include "minidump/minidump_extensions.h"
+#include "minidump/minidump_file_writer.h"
+#include "minidump/minidump_stream_writer.h"
+#include "minidump/test/minidump_file_writer_test_util.h"
+#include "minidump/test/minidump_memory_writer_test_util.h"
+#include "minidump/test/minidump_writable_test_util.h"
+#include "snapshot/test/test_memory_snapshot.h"
+#include "util/file/string_file.h"
+#include "util/stdlib/pointer_container.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+const MinidumpStreamType kBogusStreamType =
+ static_cast<MinidumpStreamType>(1234);
+
+// expected_streams is the expected number of streams in the file. The memory
+// list must be the last stream. If there is another stream, it must come first,
+// have stream type kBogusStreamType, and have zero-length data.
+void GetMemoryListStream(const std::string& file_contents,
+ const MINIDUMP_MEMORY_LIST** memory_list,
+ const uint32_t expected_streams) {
+ const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
+ const size_t kMemoryListStreamOffset =
+ kDirectoryOffset + expected_streams * sizeof(MINIDUMP_DIRECTORY);
+ const size_t kMemoryDescriptorsOffset =
+ kMemoryListStreamOffset + sizeof(MINIDUMP_MEMORY_LIST);
+
+ ASSERT_GE(file_contents.size(), kMemoryDescriptorsOffset);
+
+ const MINIDUMP_DIRECTORY* directory;
+ const MINIDUMP_HEADER* header =
+ MinidumpHeaderAtStart(file_contents, &directory);
+ ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, expected_streams, 0));
+ ASSERT_TRUE(directory);
+
+ size_t directory_index = 0;
+ if (expected_streams > 1) {
+ ASSERT_EQ(kBogusStreamType, directory[directory_index].StreamType);
+ ASSERT_EQ(0u, directory[directory_index].Location.DataSize);
+ ASSERT_EQ(kMemoryListStreamOffset, directory[directory_index].Location.Rva);
+ ++directory_index;
+ }
+
+ ASSERT_EQ(kMinidumpStreamTypeMemoryList,
+ directory[directory_index].StreamType);
+ EXPECT_EQ(kMemoryListStreamOffset, directory[directory_index].Location.Rva);
+
+ *memory_list = MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>(
+ file_contents, directory[directory_index].Location);
+ ASSERT_TRUE(memory_list);
+}
+
+TEST(MinidumpMemoryWriter, EmptyMemoryList) {
+ MinidumpFileWriter minidump_file_writer;
+ auto memory_list_writer = make_scoped_ptr(new MinidumpMemoryListWriter());
+
+ minidump_file_writer.AddStream(memory_list_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
+ sizeof(MINIDUMP_MEMORY_LIST),
+ string_file.string().size());
+
+ const MINIDUMP_MEMORY_LIST* memory_list = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ GetMemoryListStream(string_file.string(), &memory_list, 1));
+
+ EXPECT_EQ(0u, memory_list->NumberOfMemoryRanges);
+}
+
+TEST(MinidumpMemoryWriter, OneMemoryRegion) {
+ MinidumpFileWriter minidump_file_writer;
+ auto memory_list_writer = make_scoped_ptr(new MinidumpMemoryListWriter());
+
+ const uint64_t kBaseAddress = 0xfedcba9876543210;
+ const uint64_t kSize = 0x1000;
+ const uint8_t kValue = 'm';
+
+ auto memory_writer = make_scoped_ptr(
+ new TestMinidumpMemoryWriter(kBaseAddress, kSize, kValue));
+ memory_list_writer->AddMemory(memory_writer.Pass());
+
+ minidump_file_writer.AddStream(memory_list_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_MEMORY_LIST* memory_list = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ GetMemoryListStream(string_file.string(), &memory_list, 1));
+
+ MINIDUMP_MEMORY_DESCRIPTOR expected;
+ expected.StartOfMemoryRange = kBaseAddress;
+ expected.Memory.DataSize = kSize;
+ expected.Memory.Rva =
+ sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
+ sizeof(MINIDUMP_MEMORY_LIST) +
+ memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR);
+ ExpectMinidumpMemoryDescriptorAndContents(&expected,
+ &memory_list->MemoryRanges[0],
+ string_file.string(),
+ kValue,
+ true);
+}
+
+TEST(MinidumpMemoryWriter, TwoMemoryRegions) {
+ MinidumpFileWriter minidump_file_writer;
+ auto memory_list_writer = make_scoped_ptr(new MinidumpMemoryListWriter());
+
+ const uint64_t kBaseAddress0 = 0xc0ffee;
+ const uint64_t kSize0 = 0x0100;
+ const uint8_t kValue0 = '6';
+ const uint64_t kBaseAddress1 = 0xfac00fac;
+ const uint64_t kSize1 = 0x0200;
+ const uint8_t kValue1 = '!';
+
+ auto memory_writer_0 = make_scoped_ptr(
+ new TestMinidumpMemoryWriter(kBaseAddress0, kSize0, kValue0));
+ memory_list_writer->AddMemory(memory_writer_0.Pass());
+ auto memory_writer_1 = make_scoped_ptr(
+ new TestMinidumpMemoryWriter(kBaseAddress1, kSize1, kValue1));
+ memory_list_writer->AddMemory(memory_writer_1.Pass());
+
+ minidump_file_writer.AddStream(memory_list_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_MEMORY_LIST* memory_list = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ GetMemoryListStream(string_file.string(), &memory_list, 1));
+
+ EXPECT_EQ(2u, memory_list->NumberOfMemoryRanges);
+
+ MINIDUMP_MEMORY_DESCRIPTOR expected;
+
+ {
+ SCOPED_TRACE("region 0");
+
+ expected.StartOfMemoryRange = kBaseAddress0;
+ expected.Memory.DataSize = kSize0;
+ expected.Memory.Rva =
+ sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
+ sizeof(MINIDUMP_MEMORY_LIST) +
+ memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR);
+ ExpectMinidumpMemoryDescriptorAndContents(&expected,
+ &memory_list->MemoryRanges[0],
+ string_file.string(),
+ kValue0,
+ false);
+ }
+
+ {
+ SCOPED_TRACE("region 1");
+
+ expected.StartOfMemoryRange = kBaseAddress1;
+ expected.Memory.DataSize = kSize1;
+ expected.Memory.Rva = memory_list->MemoryRanges[0].Memory.Rva +
+ memory_list->MemoryRanges[0].Memory.DataSize;
+ ExpectMinidumpMemoryDescriptorAndContents(&expected,
+ &memory_list->MemoryRanges[1],
+ string_file.string(),
+ kValue1,
+ true);
+ }
+}
+
+class TestMemoryStream final : public internal::MinidumpStreamWriter {
+ public:
+ TestMemoryStream(uint64_t base_address, size_t size, uint8_t value)
+ : MinidumpStreamWriter(), memory_(base_address, size, value) {}
+
+ ~TestMemoryStream() override {}
+
+ TestMinidumpMemoryWriter* memory() {
+ return &memory_;
+ }
+
+ // MinidumpStreamWriter:
+ MinidumpStreamType StreamType() const override {
+ return kBogusStreamType;
+ }
+
+ protected:
+ // MinidumpWritable:
+ size_t SizeOfObject() override {
+ EXPECT_GE(state(), kStateFrozen);
+ return 0;
+ }
+
+ std::vector<MinidumpWritable*> Children() override {
+ EXPECT_GE(state(), kStateFrozen);
+ std::vector<MinidumpWritable*> children(1, memory());
+ return children;
+ }
+
+ bool WriteObject(FileWriterInterface* file_writer) override {
+ EXPECT_EQ(kStateWritable, state());
+ return true;
+ }
+
+ private:
+ TestMinidumpMemoryWriter memory_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestMemoryStream);
+};
+
+TEST(MinidumpMemoryWriter, ExtraMemory) {
+ // This tests MinidumpMemoryListWriter::AddExtraMemory(). That method adds
+ // a MinidumpMemoryWriter to the MinidumpMemoryListWriter without making the
+ // memory writer a child of the memory list writer.
+ MinidumpFileWriter minidump_file_writer;
+
+ const uint64_t kBaseAddress0 = 0x1000;
+ const size_t kSize0 = 0x0400;
+ const uint8_t kValue0 = '1';
+ auto test_memory_stream =
+ make_scoped_ptr(new TestMemoryStream(kBaseAddress0, kSize0, kValue0));
+
+ auto memory_list_writer = make_scoped_ptr(new MinidumpMemoryListWriter());
+ memory_list_writer->AddExtraMemory(test_memory_stream->memory());
+
+ minidump_file_writer.AddStream(test_memory_stream.Pass());
+
+ const uint64_t kBaseAddress1 = 0x2000;
+ const size_t kSize1 = 0x0400;
+ const uint8_t kValue1 = 'm';
+
+ auto memory_writer = make_scoped_ptr(
+ new TestMinidumpMemoryWriter(kBaseAddress1, kSize1, kValue1));
+ memory_list_writer->AddMemory(memory_writer.Pass());
+
+ minidump_file_writer.AddStream(memory_list_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_MEMORY_LIST* memory_list = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ GetMemoryListStream(string_file.string(), &memory_list, 2));
+
+ EXPECT_EQ(2u, memory_list->NumberOfMemoryRanges);
+
+ MINIDUMP_MEMORY_DESCRIPTOR expected;
+
+ {
+ SCOPED_TRACE("region 0");
+
+ expected.StartOfMemoryRange = kBaseAddress0;
+ expected.Memory.DataSize = kSize0;
+ expected.Memory.Rva =
+ sizeof(MINIDUMP_HEADER) + 2 * sizeof(MINIDUMP_DIRECTORY) +
+ sizeof(MINIDUMP_MEMORY_LIST) +
+ memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR);
+ ExpectMinidumpMemoryDescriptorAndContents(&expected,
+ &memory_list->MemoryRanges[0],
+ string_file.string(),
+ kValue0,
+ false);
+ }
+
+ {
+ SCOPED_TRACE("region 1");
+
+ expected.StartOfMemoryRange = kBaseAddress1;
+ expected.Memory.DataSize = kSize1;
+ expected.Memory.Rva = memory_list->MemoryRanges[0].Memory.Rva +
+ memory_list->MemoryRanges[0].Memory.DataSize;
+ ExpectMinidumpMemoryDescriptorAndContents(&expected,
+ &memory_list->MemoryRanges[1],
+ string_file.string(),
+ kValue1,
+ true);
+ }
+}
+
+TEST(MinidumpMemoryWriter, AddFromSnapshot) {
+ MINIDUMP_MEMORY_DESCRIPTOR expect_memory_descriptors[3] = {};
+ uint8_t values[arraysize(expect_memory_descriptors)] = {};
+
+ expect_memory_descriptors[0].StartOfMemoryRange = 0;
+ expect_memory_descriptors[0].Memory.DataSize = 0x1000;
+ values[0] = 0x01;
+
+ expect_memory_descriptors[1].StartOfMemoryRange = 0x1000;
+ expect_memory_descriptors[1].Memory.DataSize = 0x2000;
+ values[1] = 0xf4;
+
+ expect_memory_descriptors[2].StartOfMemoryRange = 0x7654321000000000;
+ expect_memory_descriptors[2].Memory.DataSize = 0x800;
+ values[2] = 0xa9;
+
+ PointerVector<TestMemorySnapshot> memory_snapshots_owner;
+ std::vector<const MemorySnapshot*> memory_snapshots;
+ for (size_t index = 0;
+ index < arraysize(expect_memory_descriptors);
+ ++index) {
+ TestMemorySnapshot* memory_snapshot = new TestMemorySnapshot();
+ memory_snapshots_owner.push_back(memory_snapshot);
+ memory_snapshot->SetAddress(
+ expect_memory_descriptors[index].StartOfMemoryRange);
+ memory_snapshot->SetSize(expect_memory_descriptors[index].Memory.DataSize);
+ memory_snapshot->SetValue(values[index]);
+ memory_snapshots.push_back(memory_snapshot);
+ }
+
+ auto memory_list_writer = make_scoped_ptr(new MinidumpMemoryListWriter());
+ memory_list_writer->AddFromSnapshot(memory_snapshots);
+
+ MinidumpFileWriter minidump_file_writer;
+ minidump_file_writer.AddStream(memory_list_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_MEMORY_LIST* memory_list = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ GetMemoryListStream(string_file.string(), &memory_list, 1));
+
+ ASSERT_EQ(3u, memory_list->NumberOfMemoryRanges);
+
+ for (size_t index = 0; index < memory_list->NumberOfMemoryRanges; ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index));
+ ExpectMinidumpMemoryDescriptorAndContents(
+ &expect_memory_descriptors[index],
+ &memory_list->MemoryRanges[index],
+ string_file.string(),
+ values[index],
+ index == memory_list->NumberOfMemoryRanges - 1);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer.cc
new file mode 100644
index 00000000000..4d3b1435488
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer.cc
@@ -0,0 +1,369 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_misc_info_writer.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "minidump/minidump_writer_util.h"
+#include "package.h"
+#include "snapshot/process_snapshot.h"
+#include "snapshot/system_snapshot.h"
+#include "util/file/file_writer.h"
+#include "util/numeric/in_range_cast.h"
+#include "util/numeric/safe_assignment.h"
+
+#if defined(OS_MACOSX)
+#include <AvailabilityMacros.h>
+#endif
+
+namespace crashpad {
+namespace {
+
+uint32_t TimevalToRoundedSeconds(const timeval& tv) {
+ uint32_t seconds =
+ InRangeCast<uint32_t>(tv.tv_sec, std::numeric_limits<uint32_t>::max());
+ const int kMicrosecondsPerSecond = static_cast<int>(1E6);
+ if (tv.tv_usec >= kMicrosecondsPerSecond / 2 &&
+ seconds != std::numeric_limits<uint32_t>::max()) {
+ ++seconds;
+ }
+ return seconds;
+}
+
+// For MINIDUMP_MISC_INFO_4::BuildString. dbghelp only places OS version
+// information here, but if a machine description is also available, this is the
+// only reasonable place in a minidump file to put it.
+std::string BuildString(const SystemSnapshot* system_snapshot) {
+ std::string os_version_full = system_snapshot->OSVersionFull();
+ std::string machine_description = system_snapshot->MachineDescription();
+ if (!os_version_full.empty()) {
+ if (!machine_description.empty()) {
+ return base::StringPrintf(
+ "%s; %s", os_version_full.c_str(), machine_description.c_str());
+ }
+ return os_version_full;
+ }
+ return machine_description;
+}
+
+#if defined(OS_MACOSX)
+// Converts the value of the MAC_OS_VERSION_MIN_REQUIRED or
+// MAC_OS_X_VERSION_MAX_ALLOWED macro from <AvailabilityMacros.h> to a number
+// identifying the minor Mac OS X version that it represents. For example, with
+// an argument of MAC_OS_X_VERSION_10_6, this function will return 6.
+int AvailabilityVersionToMacOSXMinorVersion(int availability) {
+ // Through MAC_OS_X_VERSION_10_9, the minor version is the tens digit.
+ if (availability >= 1000 && availability <= 1099) {
+ return (availability / 10) % 10;
+ }
+
+ // After MAC_OS_X_VERSION_10_9, the older format was insufficient to represent
+ // versions. Since then, the minor version is the thousands and hundreds
+ // digits.
+ if (availability >= 100000 && availability <= 109999) {
+ return (availability / 100) % 100;
+ }
+
+ return 0;
+}
+#endif
+
+} // namespace
+
+namespace internal {
+
+// For MINIDUMP_MISC_INFO_4::DbgBldStr. dbghelp produces strings like
+// “dbghelp.i386,6.3.9600.16520” and “dbghelp.amd64,6.3.9600.16520”. Mimic that
+// format, and add the OS that wrote the minidump along with any relevant
+// platform-specific data describing the compilation environment.
+std::string MinidumpMiscInfoDebugBuildString() {
+ // Caution: the minidump file format only has room for 39 UTF-16 code units
+ // plus a UTF-16 NUL terminator. Don’t let strings get longer than this, or
+ // they will be truncated and a message will be logged.
+#if defined(OS_MACOSX)
+ const char kOS[] = "mac";
+#elif defined(OS_LINUX)
+ const char kOS[] = "linux";
+#elif defined(OS_WIN)
+ const char kOS[] = "win";
+#else
+#error define kOS for this operating system
+#endif
+
+#if defined(ARCH_CPU_X86)
+ const char kCPU[] = "i386";
+#elif defined(ARCH_CPU_X86_64)
+ const char kCPU[] = "amd64";
+#else
+#error define kCPU for this CPU
+#endif
+
+ std::string debug_build_string = base::StringPrintf("%s.%s,%s,%s",
+ PACKAGE_TARNAME,
+ kCPU,
+ PACKAGE_VERSION,
+ kOS);
+
+#if defined(OS_MACOSX)
+ debug_build_string += base::StringPrintf(
+ ",%d,%d",
+ AvailabilityVersionToMacOSXMinorVersion(MAC_OS_X_VERSION_MIN_REQUIRED),
+ AvailabilityVersionToMacOSXMinorVersion(MAC_OS_X_VERSION_MAX_ALLOWED));
+#endif
+
+ return debug_build_string;
+}
+
+} // namespace internal
+
+MinidumpMiscInfoWriter::MinidumpMiscInfoWriter()
+ : MinidumpStreamWriter(), misc_info_() {
+}
+
+MinidumpMiscInfoWriter::~MinidumpMiscInfoWriter() {
+}
+
+void MinidumpMiscInfoWriter::InitializeFromSnapshot(
+ const ProcessSnapshot* process_snapshot) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK_EQ(misc_info_.Flags1, 0u);
+
+ SetProcessID(InRangeCast<uint32_t>(process_snapshot->ProcessID(), 0));
+
+ const SystemSnapshot* system_snapshot = process_snapshot->System();
+
+ uint64_t current_mhz;
+ uint64_t max_mhz;
+ system_snapshot->CPUFrequency(&current_mhz, &max_mhz);
+ const uint32_t kHzPerMHz = static_cast<const uint32_t>(1E6);
+ SetProcessorPowerInfo(
+ InRangeCast<uint32_t>(current_mhz / kHzPerMHz,
+ std::numeric_limits<uint32_t>::max()),
+ InRangeCast<uint32_t>(max_mhz / kHzPerMHz,
+ std::numeric_limits<uint32_t>::max()),
+ 0,
+ 0,
+ 0);
+
+ timeval start_time;
+ process_snapshot->ProcessStartTime(&start_time);
+
+ timeval user_time;
+ timeval system_time;
+ process_snapshot->ProcessCPUTimes(&user_time, &system_time);
+
+ // Round the resource usage fields to the nearest second, because the minidump
+ // format only has one-second resolution. The start_time field is truncated
+ // instead of rounded so that the process uptime is reflected more accurately
+ // when the start time is compared to the snapshot time in the
+ // MINIDUMP_HEADER, which is also truncated, not rounded.
+ uint32_t user_seconds = TimevalToRoundedSeconds(user_time);
+ uint32_t system_seconds = TimevalToRoundedSeconds(system_time);
+
+ SetProcessTimes(start_time.tv_sec, user_seconds, system_seconds);
+
+ // This determines the system’s time zone, which may be different than the
+ // process’ notion of the time zone.
+ SystemSnapshot::DaylightSavingTimeStatus dst_status;
+ int standard_offset_seconds;
+ int daylight_offset_seconds;
+ std::string standard_name;
+ std::string daylight_name;
+ system_snapshot->TimeZone(&dst_status,
+ &standard_offset_seconds,
+ &daylight_offset_seconds,
+ &standard_name,
+ &daylight_name);
+
+ // standard_offset_seconds is seconds east of UTC, but the minidump file wants
+ // minutes west of UTC. daylight_offset_seconds is also seconds east of UTC,
+ // but the minidump file wants minutes west of the standard offset. The empty
+ // ({}) arguments are for the transition times in and out of daylight saving
+ // time. These are not determined because no API exists to do so, and the
+ // transition times may vary from year to year.
+ SetTimeZone(dst_status,
+ standard_offset_seconds / -60,
+ standard_name,
+ {},
+ 0,
+ daylight_name,
+ {},
+ (standard_offset_seconds - daylight_offset_seconds) / 60);
+
+ SetBuildString(BuildString(system_snapshot),
+ internal::MinidumpMiscInfoDebugBuildString());
+}
+
+void MinidumpMiscInfoWriter::SetProcessID(uint32_t process_id) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ misc_info_.ProcessId = process_id;
+ misc_info_.Flags1 |= MINIDUMP_MISC1_PROCESS_ID;
+}
+
+void MinidumpMiscInfoWriter::SetProcessTimes(time_t process_create_time,
+ uint32_t process_user_time,
+ uint32_t process_kernel_time) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ internal::MinidumpWriterUtil::AssignTimeT(&misc_info_.ProcessCreateTime,
+ process_create_time);
+
+ misc_info_.ProcessUserTime = process_user_time;
+ misc_info_.ProcessKernelTime = process_kernel_time;
+ misc_info_.Flags1 |= MINIDUMP_MISC1_PROCESS_TIMES;
+}
+
+void MinidumpMiscInfoWriter::SetProcessorPowerInfo(
+ uint32_t processor_max_mhz,
+ uint32_t processor_current_mhz,
+ uint32_t processor_mhz_limit,
+ uint32_t processor_max_idle_state,
+ uint32_t processor_current_idle_state) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ misc_info_.ProcessorMaxMhz = processor_max_mhz;
+ misc_info_.ProcessorCurrentMhz = processor_current_mhz;
+ misc_info_.ProcessorMhzLimit = processor_mhz_limit;
+ misc_info_.ProcessorMaxIdleState = processor_max_idle_state;
+ misc_info_.ProcessorCurrentIdleState = processor_current_idle_state;
+ misc_info_.Flags1 |= MINIDUMP_MISC1_PROCESSOR_POWER_INFO;
+}
+
+void MinidumpMiscInfoWriter::SetProcessIntegrityLevel(
+ uint32_t process_integrity_level) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ misc_info_.ProcessIntegrityLevel = process_integrity_level;
+ misc_info_.Flags1 |= MINIDUMP_MISC3_PROCESS_INTEGRITY;
+}
+
+void MinidumpMiscInfoWriter::SetProcessExecuteFlags(
+ uint32_t process_execute_flags) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ misc_info_.ProcessExecuteFlags = process_execute_flags;
+ misc_info_.Flags1 |= MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS;
+}
+
+void MinidumpMiscInfoWriter::SetProtectedProcess(uint32_t protected_process) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ misc_info_.ProtectedProcess = protected_process;
+ misc_info_.Flags1 |= MINIDUMP_MISC3_PROTECTED_PROCESS;
+}
+
+void MinidumpMiscInfoWriter::SetTimeZone(uint32_t time_zone_id,
+ int32_t bias,
+ const std::string& standard_name,
+ const SYSTEMTIME& standard_date,
+ int32_t standard_bias,
+ const std::string& daylight_name,
+ const SYSTEMTIME& daylight_date,
+ int32_t daylight_bias) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ misc_info_.TimeZoneId = time_zone_id;
+ misc_info_.TimeZone.Bias = bias;
+
+ internal::MinidumpWriterUtil::AssignUTF8ToUTF16(
+ misc_info_.TimeZone.StandardName,
+ arraysize(misc_info_.TimeZone.StandardName),
+ standard_name);
+
+ misc_info_.TimeZone.StandardDate = standard_date;
+ misc_info_.TimeZone.StandardBias = standard_bias;
+
+ internal::MinidumpWriterUtil::AssignUTF8ToUTF16(
+ misc_info_.TimeZone.DaylightName,
+ arraysize(misc_info_.TimeZone.DaylightName),
+ daylight_name);
+
+ misc_info_.TimeZone.DaylightDate = daylight_date;
+ misc_info_.TimeZone.DaylightBias = daylight_bias;
+
+ misc_info_.Flags1 |= MINIDUMP_MISC3_TIMEZONE;
+}
+
+void MinidumpMiscInfoWriter::SetBuildString(
+ const std::string& build_string,
+ const std::string& debug_build_string) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ misc_info_.Flags1 |= MINIDUMP_MISC4_BUILDSTRING;
+
+ internal::MinidumpWriterUtil::AssignUTF8ToUTF16(
+ misc_info_.BuildString, arraysize(misc_info_.BuildString), build_string);
+ internal::MinidumpWriterUtil::AssignUTF8ToUTF16(
+ misc_info_.DbgBldStr,
+ arraysize(misc_info_.DbgBldStr),
+ debug_build_string);
+}
+
+bool MinidumpMiscInfoWriter::Freeze() {
+ DCHECK_EQ(state(), kStateMutable);
+
+ if (!MinidumpStreamWriter::Freeze()) {
+ return false;
+ }
+
+ size_t size = CalculateSizeOfObjectFromFlags();
+ if (!AssignIfInRange(&misc_info_.SizeOfInfo, size)) {
+ LOG(ERROR) << "size " << size << " out of range";
+ return false;
+ }
+
+ return true;
+}
+
+size_t MinidumpMiscInfoWriter::SizeOfObject() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ return CalculateSizeOfObjectFromFlags();
+}
+
+bool MinidumpMiscInfoWriter::WriteObject(FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateWritable);
+
+ return file_writer->Write(&misc_info_, CalculateSizeOfObjectFromFlags());
+}
+
+MinidumpStreamType MinidumpMiscInfoWriter::StreamType() const {
+ return kMinidumpStreamTypeMiscInfo;
+}
+
+size_t MinidumpMiscInfoWriter::CalculateSizeOfObjectFromFlags() const {
+ DCHECK_GE(state(), kStateFrozen);
+
+ if (misc_info_.Flags1 & MINIDUMP_MISC4_BUILDSTRING) {
+ return sizeof(MINIDUMP_MISC_INFO_4);
+ }
+ if (misc_info_.Flags1 &
+ (MINIDUMP_MISC3_PROCESS_INTEGRITY | MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS |
+ MINIDUMP_MISC3_TIMEZONE | MINIDUMP_MISC3_PROTECTED_PROCESS)) {
+ return sizeof(MINIDUMP_MISC_INFO_3);
+ }
+ if (misc_info_.Flags1 & MINIDUMP_MISC1_PROCESSOR_POWER_INFO) {
+ return sizeof(MINIDUMP_MISC_INFO_2);
+ }
+ return sizeof(MINIDUMP_MISC_INFO);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer.h
new file mode 100644
index 00000000000..b7c35499ba0
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer.h
@@ -0,0 +1,132 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_MINIDUMP_MINIDUMP_MISC_INFO_WRITER_H_
+#define CRASHPAD_MINIDUMP_MINIDUMP_MISC_INFO_WRITER_H_
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "minidump/minidump_stream_writer.h"
+#include "minidump/minidump_writable.h"
+
+namespace crashpad {
+
+class ProcessSnapshot;
+
+namespace internal {
+
+//! \brief Returns the string to set in MINIDUMP_MISC_INFO_4::DbgBldStr.
+//!
+//! dbghelp produces strings like `"dbghelp.i386,6.3.9600.16520"` and
+//! `"dbghelp.amd64,6.3.9600.16520"`. This function mimics that format, and adds
+//! the OS that wrote the minidump along with any relevant platform-specific
+//! data describing the compilation environment.
+//!
+//! This function is an implementation detail of
+//! MinidumpMiscInfoWriter::InitializeFromSnapshot() and is only exposed for
+//! testing purposes.
+std::string MinidumpMiscInfoDebugBuildString();
+
+} // namespace internal
+
+//! \brief The writer for a stream in the MINIDUMP_MISC_INFO family in a
+//! minidump file.
+//!
+//! The actual stream written will be a MINIDUMP_MISC_INFO,
+//! MINIDUMP_MISC_INFO_2, MINIDUMP_MISC_INFO_3, or MINIDUMP_MISC_INFO_4 stream.
+//! Later versions of MINIDUMP_MISC_INFO are supersets of earlier versions. The
+//! earliest version that supports all of the information that an object of this
+//! class contains will be used.
+class MinidumpMiscInfoWriter final : public internal::MinidumpStreamWriter {
+ public:
+ MinidumpMiscInfoWriter();
+ ~MinidumpMiscInfoWriter() override;
+
+ //! \brief Initializes MINIDUMP_MISC_INFO_N based on \a process_snapshot.
+ //!
+ //! \param[in] process_snapshot The process snapshot to use as source data.
+ //!
+ //! \note Valid in #kStateMutable. No mutator methods may be called before
+ //! this method, and it is not normally necessary to call any mutator
+ //! methods after this method.
+ void InitializeFromSnapshot(const ProcessSnapshot* process_snapshot);
+
+ //! \brief Sets the field referenced by #MINIDUMP_MISC1_PROCESS_ID.
+ void SetProcessID(uint32_t process_id);
+
+ //! \brief Sets the fields referenced by #MINIDUMP_MISC1_PROCESS_TIMES.
+ void SetProcessTimes(time_t process_create_time,
+ uint32_t process_user_time,
+ uint32_t process_kernel_time);
+
+ //! \brief Sets the fields referenced by #MINIDUMP_MISC1_PROCESSOR_POWER_INFO.
+ void SetProcessorPowerInfo(uint32_t processor_max_mhz,
+ uint32_t processor_current_mhz,
+ uint32_t processor_mhz_limit,
+ uint32_t processor_max_idle_state,
+ uint32_t processor_current_idle_state);
+
+ //! \brief Sets the field referenced by #MINIDUMP_MISC3_PROCESS_INTEGRITY.
+ void SetProcessIntegrityLevel(uint32_t process_integrity_level);
+
+ //! \brief Sets the field referenced by #MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS.
+ void SetProcessExecuteFlags(uint32_t process_execute_flags);
+
+ //! \brief Sets the field referenced by #MINIDUMP_MISC3_PROTECTED_PROCESS.
+ void SetProtectedProcess(uint32_t protected_process);
+
+ //! \brief Sets the fields referenced by #MINIDUMP_MISC3_TIMEZONE.
+ void SetTimeZone(uint32_t time_zone_id,
+ int32_t bias,
+ const std::string& standard_name,
+ const SYSTEMTIME& standard_date,
+ int32_t standard_bias,
+ const std::string& daylight_name,
+ const SYSTEMTIME& daylight_date,
+ int32_t daylight_bias);
+
+ //! \brief Sets the fields referenced by #MINIDUMP_MISC4_BUILDSTRING.
+ void SetBuildString(const std::string& build_string,
+ const std::string& debug_build_string);
+
+ protected:
+ // MinidumpWritable:
+ bool Freeze() override;
+ size_t SizeOfObject() override;
+ bool WriteObject(FileWriterInterface* file_writer) override;
+ MinidumpStreamType StreamType() const override;
+
+ private:
+ //! \brief Returns the size of the object to be written based on
+ //! MINIDUMP_MISC_INFO_N::Flags1.
+ //!
+ //! The smallest defined structure type in the MINIDUMP_MISC_INFO family that
+ //! can hold all of the data that has been populated will be used.
+ size_t CalculateSizeOfObjectFromFlags() const;
+
+ MINIDUMP_MISC_INFO_N misc_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpMiscInfoWriter);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_MINIDUMP_MINIDUMP_MISC_INFO_WRITER_H_
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer_test.cc
new file mode 100644
index 00000000000..5e5fa926182
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer_test.cc
@@ -0,0 +1,723 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_misc_info_writer.h"
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <string.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "gtest/gtest.h"
+#include "minidump/minidump_file_writer.h"
+#include "minidump/test/minidump_file_writer_test_util.h"
+#include "minidump/test/minidump_writable_test_util.h"
+#include "snapshot/test/test_process_snapshot.h"
+#include "snapshot/test/test_system_snapshot.h"
+#include "util/file/string_file.h"
+#include "util/stdlib/strlcpy.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+template <typename T>
+void GetMiscInfoStream(const std::string& file_contents, const T** misc_info) {
+ const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
+ const size_t kMiscInfoStreamOffset =
+ kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);
+ const size_t kMiscInfoStreamSize = sizeof(T);
+ const size_t kFileSize = kMiscInfoStreamOffset + kMiscInfoStreamSize;
+
+ ASSERT_EQ(kFileSize, file_contents.size());
+
+ const MINIDUMP_DIRECTORY* directory;
+ const MINIDUMP_HEADER* header =
+ MinidumpHeaderAtStart(file_contents, &directory);
+ ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));
+ ASSERT_TRUE(directory);
+
+ ASSERT_EQ(kMinidumpStreamTypeMiscInfo, directory[0].StreamType);
+ EXPECT_EQ(kMiscInfoStreamOffset, directory[0].Location.Rva);
+
+ *misc_info = MinidumpWritableAtLocationDescriptor<T>(file_contents,
+ directory[0].Location);
+ ASSERT_TRUE(misc_info);
+}
+
+void ExpectNULPaddedString16Equal(const base::char16* expected,
+ const base::char16* observed,
+ size_t size) {
+ base::string16 expected_string(expected, size);
+ base::string16 observed_string(observed, size);
+ EXPECT_EQ(expected_string, observed_string);
+}
+
+void ExpectSystemTimeEqual(const SYSTEMTIME* expected,
+ const SYSTEMTIME* observed) {
+ EXPECT_EQ(expected->wYear, observed->wYear);
+ EXPECT_EQ(expected->wMonth, observed->wMonth);
+ EXPECT_EQ(expected->wDayOfWeek, observed->wDayOfWeek);
+ EXPECT_EQ(expected->wDay, observed->wDay);
+ EXPECT_EQ(expected->wHour, observed->wHour);
+ EXPECT_EQ(expected->wMinute, observed->wMinute);
+ EXPECT_EQ(expected->wSecond, observed->wSecond);
+ EXPECT_EQ(expected->wMilliseconds, observed->wMilliseconds);
+}
+
+template <typename T>
+void ExpectMiscInfoEqual(const T* expected, const T* observed);
+
+template <>
+void ExpectMiscInfoEqual<MINIDUMP_MISC_INFO>(
+ const MINIDUMP_MISC_INFO* expected,
+ const MINIDUMP_MISC_INFO* observed) {
+ EXPECT_EQ(expected->Flags1, observed->Flags1);
+ EXPECT_EQ(expected->ProcessId, observed->ProcessId);
+ EXPECT_EQ(expected->ProcessCreateTime, observed->ProcessCreateTime);
+ EXPECT_EQ(expected->ProcessUserTime, observed->ProcessUserTime);
+ EXPECT_EQ(expected->ProcessKernelTime, observed->ProcessKernelTime);
+}
+
+template <>
+void ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_2>(
+ const MINIDUMP_MISC_INFO_2* expected,
+ const MINIDUMP_MISC_INFO_2* observed) {
+ ExpectMiscInfoEqual<MINIDUMP_MISC_INFO>(
+ reinterpret_cast<const MINIDUMP_MISC_INFO*>(expected),
+ reinterpret_cast<const MINIDUMP_MISC_INFO*>(observed));
+ EXPECT_EQ(expected->ProcessorMaxMhz, observed->ProcessorMaxMhz);
+ EXPECT_EQ(expected->ProcessorCurrentMhz, observed->ProcessorCurrentMhz);
+ EXPECT_EQ(expected->ProcessorMhzLimit, observed->ProcessorMhzLimit);
+ EXPECT_EQ(expected->ProcessorMaxIdleState, observed->ProcessorMaxIdleState);
+ EXPECT_EQ(expected->ProcessorCurrentIdleState,
+ observed->ProcessorCurrentIdleState);
+}
+
+template <>
+void ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_3>(
+ const MINIDUMP_MISC_INFO_3* expected,
+ const MINIDUMP_MISC_INFO_3* observed) {
+ ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_2>(
+ reinterpret_cast<const MINIDUMP_MISC_INFO_2*>(expected),
+ reinterpret_cast<const MINIDUMP_MISC_INFO_2*>(observed));
+ EXPECT_EQ(expected->ProcessIntegrityLevel, observed->ProcessIntegrityLevel);
+ EXPECT_EQ(expected->ProcessExecuteFlags, observed->ProcessExecuteFlags);
+ EXPECT_EQ(expected->ProtectedProcess, observed->ProtectedProcess);
+ EXPECT_EQ(expected->TimeZoneId, observed->TimeZoneId);
+ EXPECT_EQ(expected->TimeZone.Bias, observed->TimeZone.Bias);
+ {
+ SCOPED_TRACE("Standard");
+ ExpectNULPaddedString16Equal(expected->TimeZone.StandardName,
+ observed->TimeZone.StandardName,
+ arraysize(expected->TimeZone.StandardName));
+ ExpectSystemTimeEqual(&expected->TimeZone.StandardDate,
+ &observed->TimeZone.StandardDate);
+ EXPECT_EQ(expected->TimeZone.StandardBias, observed->TimeZone.StandardBias);
+ }
+ {
+ SCOPED_TRACE("Daylight");
+ ExpectNULPaddedString16Equal(expected->TimeZone.DaylightName,
+ observed->TimeZone.DaylightName,
+ arraysize(expected->TimeZone.DaylightName));
+ ExpectSystemTimeEqual(&expected->TimeZone.DaylightDate,
+ &observed->TimeZone.DaylightDate);
+ EXPECT_EQ(expected->TimeZone.DaylightBias, observed->TimeZone.DaylightBias);
+ }
+}
+
+template <>
+void ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_4>(
+ const MINIDUMP_MISC_INFO_4* expected,
+ const MINIDUMP_MISC_INFO_4* observed) {
+ ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_3>(
+ reinterpret_cast<const MINIDUMP_MISC_INFO_3*>(expected),
+ reinterpret_cast<const MINIDUMP_MISC_INFO_3*>(observed));
+ {
+ SCOPED_TRACE("BuildString");
+ ExpectNULPaddedString16Equal(expected->BuildString,
+ observed->BuildString,
+ arraysize(expected->BuildString));
+ }
+ {
+ SCOPED_TRACE("DbgBldStr");
+ ExpectNULPaddedString16Equal(expected->DbgBldStr,
+ observed->DbgBldStr,
+ arraysize(expected->DbgBldStr));
+ }
+}
+
+TEST(MinidumpMiscInfoWriter, Empty) {
+ MinidumpFileWriter minidump_file_writer;
+ auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter());
+
+ minidump_file_writer.AddStream(misc_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_MISC_INFO* observed = nullptr;
+ ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));
+
+ MINIDUMP_MISC_INFO expected = {};
+
+ ExpectMiscInfoEqual(&expected, observed);
+}
+
+TEST(MinidumpMiscInfoWriter, ProcessId) {
+ MinidumpFileWriter minidump_file_writer;
+ auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter());
+
+ const uint32_t kProcessId = 12345;
+
+ misc_info_writer->SetProcessID(kProcessId);
+
+ minidump_file_writer.AddStream(misc_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_MISC_INFO* observed = nullptr;
+ ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));
+
+ MINIDUMP_MISC_INFO expected = {};
+ expected.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
+ expected.ProcessId = kProcessId;
+
+ ExpectMiscInfoEqual(&expected, observed);
+}
+
+TEST(MinidumpMiscInfoWriter, ProcessTimes) {
+ MinidumpFileWriter minidump_file_writer;
+ auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter());
+
+ const time_t kProcessCreateTime = 0x15252f00;
+ const uint32_t kProcessUserTime = 10;
+ const uint32_t kProcessKernelTime = 5;
+
+ misc_info_writer->SetProcessTimes(
+ kProcessCreateTime, kProcessUserTime, kProcessKernelTime);
+
+ minidump_file_writer.AddStream(misc_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_MISC_INFO* observed = nullptr;
+ ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));
+
+ MINIDUMP_MISC_INFO expected = {};
+ expected.Flags1 = MINIDUMP_MISC1_PROCESS_TIMES;
+ expected.ProcessCreateTime = kProcessCreateTime;
+ expected.ProcessUserTime = kProcessUserTime;
+ expected.ProcessKernelTime = kProcessKernelTime;
+
+ ExpectMiscInfoEqual(&expected, observed);
+}
+
+TEST(MinidumpMiscInfoWriter, ProcessorPowerInfo) {
+ MinidumpFileWriter minidump_file_writer;
+ auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter());
+
+ const uint32_t kProcessorMaxMhz = 2800;
+ const uint32_t kProcessorCurrentMhz = 2300;
+ const uint32_t kProcessorMhzLimit = 3300;
+ const uint32_t kProcessorMaxIdleState = 5;
+ const uint32_t kProcessorCurrentIdleState = 1;
+
+ misc_info_writer->SetProcessorPowerInfo(kProcessorMaxMhz,
+ kProcessorCurrentMhz,
+ kProcessorMhzLimit,
+ kProcessorMaxIdleState,
+ kProcessorCurrentIdleState);
+
+ minidump_file_writer.AddStream(misc_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_MISC_INFO_2* observed = nullptr;
+ ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));
+
+ MINIDUMP_MISC_INFO_2 expected = {};
+ expected.Flags1 = MINIDUMP_MISC1_PROCESSOR_POWER_INFO;
+ expected.ProcessorMaxMhz = kProcessorMaxMhz;
+ expected.ProcessorCurrentMhz = kProcessorCurrentMhz;
+ expected.ProcessorMhzLimit = kProcessorMhzLimit;
+ expected.ProcessorMaxIdleState = kProcessorMaxIdleState;
+ expected.ProcessorCurrentIdleState = kProcessorCurrentIdleState;
+
+ ExpectMiscInfoEqual(&expected, observed);
+}
+
+TEST(MinidumpMiscInfoWriter, ProcessIntegrityLevel) {
+ MinidumpFileWriter minidump_file_writer;
+ auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter());
+
+ const uint32_t kProcessIntegrityLevel = 0x2000;
+
+ misc_info_writer->SetProcessIntegrityLevel(kProcessIntegrityLevel);
+
+ minidump_file_writer.AddStream(misc_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_MISC_INFO_3* observed = nullptr;
+ ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));
+
+ MINIDUMP_MISC_INFO_3 expected = {};
+ expected.Flags1 = MINIDUMP_MISC3_PROCESS_INTEGRITY;
+ expected.ProcessIntegrityLevel = kProcessIntegrityLevel;
+
+ ExpectMiscInfoEqual(&expected, observed);
+}
+
+TEST(MinidumpMiscInfoWriter, ProcessExecuteFlags) {
+ MinidumpFileWriter minidump_file_writer;
+ auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter());
+
+ const uint32_t kProcessExecuteFlags = 0x13579bdf;
+
+ misc_info_writer->SetProcessExecuteFlags(kProcessExecuteFlags);
+
+ minidump_file_writer.AddStream(misc_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_MISC_INFO_3* observed = nullptr;
+ ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));
+
+ MINIDUMP_MISC_INFO_3 expected = {};
+ expected.Flags1 = MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS;
+ expected.ProcessExecuteFlags = kProcessExecuteFlags;
+
+ ExpectMiscInfoEqual(&expected, observed);
+}
+
+TEST(MinidumpMiscInfoWriter, ProtectedProcess) {
+ MinidumpFileWriter minidump_file_writer;
+ auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter());
+
+ const uint32_t kProtectedProcess = 1;
+
+ misc_info_writer->SetProtectedProcess(kProtectedProcess);
+
+ minidump_file_writer.AddStream(misc_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_MISC_INFO_3* observed = nullptr;
+ ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));
+
+ MINIDUMP_MISC_INFO_3 expected = {};
+ expected.Flags1 = MINIDUMP_MISC3_PROTECTED_PROCESS;
+ expected.ProtectedProcess = kProtectedProcess;
+
+ ExpectMiscInfoEqual(&expected, observed);
+}
+
+TEST(MinidumpMiscInfoWriter, TimeZone) {
+ MinidumpFileWriter minidump_file_writer;
+ auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter());
+
+ const uint32_t kTimeZoneId = 2;
+ const int32_t kBias = 300;
+ const char kStandardName[] = "EST";
+ const SYSTEMTIME kStandardDate = {0, 11, 1, 0, 2, 0, 0, 0};
+ const int32_t kStandardBias = 0;
+ const char kDaylightName[] = "EDT";
+ const SYSTEMTIME kDaylightDate = {0, 3, 2, 0, 2, 0, 0, 0};
+ const int32_t kDaylightBias = -60;
+
+ misc_info_writer->SetTimeZone(kTimeZoneId,
+ kBias,
+ kStandardName,
+ kStandardDate,
+ kStandardBias,
+ kDaylightName,
+ kDaylightDate,
+ kDaylightBias);
+
+ minidump_file_writer.AddStream(misc_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_MISC_INFO_3* observed = nullptr;
+ ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));
+
+ MINIDUMP_MISC_INFO_3 expected = {};
+ expected.Flags1 = MINIDUMP_MISC3_TIMEZONE;
+ expected.TimeZoneId = kTimeZoneId;
+ expected.TimeZone.Bias = kBias;
+ base::string16 standard_name_utf16 = base::UTF8ToUTF16(kStandardName);
+ c16lcpy(expected.TimeZone.StandardName,
+ standard_name_utf16.c_str(),
+ arraysize(expected.TimeZone.StandardName));
+ memcpy(&expected.TimeZone.StandardDate,
+ &kStandardDate,
+ sizeof(expected.TimeZone.StandardDate));
+ expected.TimeZone.StandardBias = kStandardBias;
+ base::string16 daylight_name_utf16 = base::UTF8ToUTF16(kDaylightName);
+ c16lcpy(expected.TimeZone.DaylightName,
+ daylight_name_utf16.c_str(),
+ arraysize(expected.TimeZone.DaylightName));
+ memcpy(&expected.TimeZone.DaylightDate,
+ &kDaylightDate,
+ sizeof(expected.TimeZone.DaylightDate));
+ expected.TimeZone.DaylightBias = kDaylightBias;
+
+ ExpectMiscInfoEqual(&expected, observed);
+}
+
+TEST(MinidumpMiscInfoWriter, TimeZoneStringsOverflow) {
+ // This test makes sure that the time zone name strings are truncated properly
+ // to the widths of their fields.
+
+ MinidumpFileWriter minidump_file_writer;
+ auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter());
+
+ const uint32_t kTimeZoneId = 2;
+ const int32_t kBias = 300;
+ MINIDUMP_MISC_INFO_N tmp;
+ ALLOW_UNUSED_LOCAL(tmp);
+ std::string standard_name(arraysize(tmp.TimeZone.StandardName) + 1, 's');
+ const int32_t kStandardBias = 0;
+ std::string daylight_name(arraysize(tmp.TimeZone.DaylightName), 'd');
+ const int32_t kDaylightBias = -60;
+
+ // Test using kSystemTimeZero, because not all platforms will be able to
+ // provide daylight saving time transition times.
+ const SYSTEMTIME kSystemTimeZero = {};
+
+ misc_info_writer->SetTimeZone(kTimeZoneId,
+ kBias,
+ standard_name,
+ kSystemTimeZero,
+ kStandardBias,
+ daylight_name,
+ kSystemTimeZero,
+ kDaylightBias);
+
+ minidump_file_writer.AddStream(misc_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_MISC_INFO_3* observed = nullptr;
+ ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));
+
+ MINIDUMP_MISC_INFO_3 expected = {};
+ expected.Flags1 = MINIDUMP_MISC3_TIMEZONE;
+ expected.TimeZoneId = kTimeZoneId;
+ expected.TimeZone.Bias = kBias;
+ base::string16 standard_name_utf16 = base::UTF8ToUTF16(standard_name);
+ c16lcpy(expected.TimeZone.StandardName,
+ standard_name_utf16.c_str(),
+ arraysize(expected.TimeZone.StandardName));
+ memcpy(&expected.TimeZone.StandardDate,
+ &kSystemTimeZero,
+ sizeof(expected.TimeZone.StandardDate));
+ expected.TimeZone.StandardBias = kStandardBias;
+ base::string16 daylight_name_utf16 = base::UTF8ToUTF16(daylight_name);
+ c16lcpy(expected.TimeZone.DaylightName,
+ daylight_name_utf16.c_str(),
+ arraysize(expected.TimeZone.DaylightName));
+ memcpy(&expected.TimeZone.DaylightDate,
+ &kSystemTimeZero,
+ sizeof(expected.TimeZone.DaylightDate));
+ expected.TimeZone.DaylightBias = kDaylightBias;
+
+ ExpectMiscInfoEqual(&expected, observed);
+}
+
+TEST(MinidumpMiscInfoWriter, BuildStrings) {
+ MinidumpFileWriter minidump_file_writer;
+ auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter());
+
+ const char kBuildString[] = "build string";
+ const char kDebugBuildString[] = "debug build string";
+
+ misc_info_writer->SetBuildString(kBuildString, kDebugBuildString);
+
+ minidump_file_writer.AddStream(misc_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_MISC_INFO_4* observed = nullptr;
+ ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));
+
+ MINIDUMP_MISC_INFO_4 expected = {};
+ expected.Flags1 = MINIDUMP_MISC4_BUILDSTRING;
+ base::string16 build_string_utf16 = base::UTF8ToUTF16(kBuildString);
+ c16lcpy(expected.BuildString,
+ build_string_utf16.c_str(),
+ arraysize(expected.BuildString));
+ base::string16 debug_build_string_utf16 =
+ base::UTF8ToUTF16(kDebugBuildString);
+ c16lcpy(expected.DbgBldStr,
+ debug_build_string_utf16.c_str(),
+ arraysize(expected.DbgBldStr));
+
+ ExpectMiscInfoEqual(&expected, observed);
+}
+
+TEST(MinidumpMiscInfoWriter, BuildStringsOverflow) {
+ // This test makes sure that the build strings are truncated properly to the
+ // widths of their fields.
+
+ MinidumpFileWriter minidump_file_writer;
+ auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter());
+
+ MINIDUMP_MISC_INFO_4 tmp;
+ ALLOW_UNUSED_LOCAL(tmp);
+ std::string build_string(arraysize(tmp.BuildString) + 1, 'B');
+ std::string debug_build_string(arraysize(tmp.DbgBldStr), 'D');
+
+ misc_info_writer->SetBuildString(build_string, debug_build_string);
+
+ minidump_file_writer.AddStream(misc_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_MISC_INFO_4* observed = nullptr;
+ ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));
+
+ MINIDUMP_MISC_INFO_4 expected = {};
+ expected.Flags1 = MINIDUMP_MISC4_BUILDSTRING;
+ base::string16 build_string_utf16 = base::UTF8ToUTF16(build_string);
+ c16lcpy(expected.BuildString,
+ build_string_utf16.c_str(),
+ arraysize(expected.BuildString));
+ base::string16 debug_build_string_utf16 =
+ base::UTF8ToUTF16(debug_build_string);
+ c16lcpy(expected.DbgBldStr,
+ debug_build_string_utf16.c_str(),
+ arraysize(expected.DbgBldStr));
+
+ ExpectMiscInfoEqual(&expected, observed);
+}
+
+TEST(MinidumpMiscInfoWriter, Everything) {
+ MinidumpFileWriter minidump_file_writer;
+ auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter());
+
+ const uint32_t kProcessId = 12345;
+ const time_t kProcessCreateTime = 0x15252f00;
+ const uint32_t kProcessUserTime = 10;
+ const uint32_t kProcessKernelTime = 5;
+ const uint32_t kProcessorMaxMhz = 2800;
+ const uint32_t kProcessorCurrentMhz = 2300;
+ const uint32_t kProcessorMhzLimit = 3300;
+ const uint32_t kProcessorMaxIdleState = 5;
+ const uint32_t kProcessorCurrentIdleState = 1;
+ const uint32_t kProcessIntegrityLevel = 0x2000;
+ const uint32_t kProcessExecuteFlags = 0x13579bdf;
+ const uint32_t kProtectedProcess = 1;
+ const uint32_t kTimeZoneId = 2;
+ const int32_t kBias = 300;
+ const char kStandardName[] = "EST";
+ const int32_t kStandardBias = 0;
+ const char kDaylightName[] = "EDT";
+ const int32_t kDaylightBias = -60;
+ const SYSTEMTIME kSystemTimeZero = {};
+ const char kBuildString[] = "build string";
+ const char kDebugBuildString[] = "debug build string";
+
+ misc_info_writer->SetProcessID(kProcessId);
+ misc_info_writer->SetProcessTimes(
+ kProcessCreateTime, kProcessUserTime, kProcessKernelTime);
+ misc_info_writer->SetProcessorPowerInfo(kProcessorMaxMhz,
+ kProcessorCurrentMhz,
+ kProcessorMhzLimit,
+ kProcessorMaxIdleState,
+ kProcessorCurrentIdleState);
+ misc_info_writer->SetProcessIntegrityLevel(kProcessIntegrityLevel);
+ misc_info_writer->SetProcessExecuteFlags(kProcessExecuteFlags);
+ misc_info_writer->SetProtectedProcess(kProtectedProcess);
+ misc_info_writer->SetTimeZone(kTimeZoneId,
+ kBias,
+ kStandardName,
+ kSystemTimeZero,
+ kStandardBias,
+ kDaylightName,
+ kSystemTimeZero,
+ kDaylightBias);
+ misc_info_writer->SetBuildString(kBuildString, kDebugBuildString);
+
+ minidump_file_writer.AddStream(misc_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_MISC_INFO_4* observed = nullptr;
+ ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));
+
+ MINIDUMP_MISC_INFO_4 expected = {};
+ expected.Flags1 =
+ MINIDUMP_MISC1_PROCESS_ID | MINIDUMP_MISC1_PROCESS_TIMES |
+ MINIDUMP_MISC1_PROCESSOR_POWER_INFO | MINIDUMP_MISC3_PROCESS_INTEGRITY |
+ MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS | MINIDUMP_MISC3_PROTECTED_PROCESS |
+ MINIDUMP_MISC3_TIMEZONE | MINIDUMP_MISC4_BUILDSTRING;
+ expected.ProcessId = kProcessId;
+ expected.ProcessCreateTime = kProcessCreateTime;
+ expected.ProcessUserTime = kProcessUserTime;
+ expected.ProcessKernelTime = kProcessKernelTime;
+ expected.ProcessorMaxMhz = kProcessorMaxMhz;
+ expected.ProcessorCurrentMhz = kProcessorCurrentMhz;
+ expected.ProcessorMhzLimit = kProcessorMhzLimit;
+ expected.ProcessorMaxIdleState = kProcessorMaxIdleState;
+ expected.ProcessorCurrentIdleState = kProcessorCurrentIdleState;
+ expected.ProcessIntegrityLevel = kProcessIntegrityLevel;
+ expected.ProcessExecuteFlags = kProcessExecuteFlags;
+ expected.ProtectedProcess = kProtectedProcess;
+ expected.TimeZoneId = kTimeZoneId;
+ expected.TimeZone.Bias = kBias;
+ base::string16 standard_name_utf16 = base::UTF8ToUTF16(kStandardName);
+ c16lcpy(expected.TimeZone.StandardName,
+ standard_name_utf16.c_str(),
+ arraysize(expected.TimeZone.StandardName));
+ memcpy(&expected.TimeZone.StandardDate,
+ &kSystemTimeZero,
+ sizeof(expected.TimeZone.StandardDate));
+ expected.TimeZone.StandardBias = kStandardBias;
+ base::string16 daylight_name_utf16 = base::UTF8ToUTF16(kDaylightName);
+ c16lcpy(expected.TimeZone.DaylightName,
+ daylight_name_utf16.c_str(),
+ arraysize(expected.TimeZone.DaylightName));
+ memcpy(&expected.TimeZone.DaylightDate,
+ &kSystemTimeZero,
+ sizeof(expected.TimeZone.DaylightDate));
+ expected.TimeZone.DaylightBias = kDaylightBias;
+ base::string16 build_string_utf16 = base::UTF8ToUTF16(kBuildString);
+ c16lcpy(expected.BuildString,
+ build_string_utf16.c_str(),
+ arraysize(expected.BuildString));
+ base::string16 debug_build_string_utf16 =
+ base::UTF8ToUTF16(kDebugBuildString);
+ c16lcpy(expected.DbgBldStr,
+ debug_build_string_utf16.c_str(),
+ arraysize(expected.DbgBldStr));
+
+ ExpectMiscInfoEqual(&expected, observed);
+}
+
+TEST(MinidumpMiscInfoWriter, InitializeFromSnapshot) {
+ MINIDUMP_MISC_INFO_4 expect_misc_info = {};
+
+ const char kStandardTimeName[] = "EST";
+ const char kDaylightTimeName[] = "EDT";
+ const char kOSVersionFull[] =
+ "Mac OS X 10.9.5 (13F34); "
+ "Darwin 13.4.0 Darwin Kernel Version 13.4.0: "
+ "Sun Aug 17 19:50:11 PDT 2014; "
+ "root:xnu-2422.115.4~1/RELEASE_X86_64 x86_64";
+ const char kMachineDescription[] = "MacBookPro11,3 (Mac-2BD1B31983FE1663)";
+ base::string16 standard_time_name_utf16 =
+ base::UTF8ToUTF16(kStandardTimeName);
+ base::string16 daylight_time_name_utf16 =
+ base::UTF8ToUTF16(kDaylightTimeName);
+ base::string16 build_string_utf16 = base::UTF8ToUTF16(
+ std::string(kOSVersionFull) + "; " + kMachineDescription);
+ std::string debug_build_string = internal::MinidumpMiscInfoDebugBuildString();
+ EXPECT_FALSE(debug_build_string.empty());
+ base::string16 debug_build_string_utf16 =
+ base::UTF8ToUTF16(debug_build_string);
+
+ expect_misc_info.SizeOfInfo = sizeof(expect_misc_info);
+ expect_misc_info.Flags1 = MINIDUMP_MISC1_PROCESS_ID |
+ MINIDUMP_MISC1_PROCESS_TIMES |
+ MINIDUMP_MISC1_PROCESSOR_POWER_INFO |
+ MINIDUMP_MISC3_TIMEZONE |
+ MINIDUMP_MISC4_BUILDSTRING;
+ expect_misc_info.ProcessId = 12345;
+ expect_misc_info.ProcessCreateTime = 0x555c7740;
+ expect_misc_info.ProcessUserTime = 60;
+ expect_misc_info.ProcessKernelTime = 15;
+ expect_misc_info.ProcessorCurrentMhz = 2800;
+ expect_misc_info.ProcessorMaxMhz = 2800;
+ expect_misc_info.TimeZoneId = 1;
+ expect_misc_info.TimeZone.Bias = 300;
+ c16lcpy(expect_misc_info.TimeZone.StandardName,
+ standard_time_name_utf16.c_str(),
+ arraysize(expect_misc_info.TimeZone.StandardName));
+ expect_misc_info.TimeZone.StandardBias = 0;
+ c16lcpy(expect_misc_info.TimeZone.DaylightName,
+ daylight_time_name_utf16.c_str(),
+ arraysize(expect_misc_info.TimeZone.DaylightName));
+ expect_misc_info.TimeZone.DaylightBias = -60;
+ c16lcpy(expect_misc_info.BuildString,
+ build_string_utf16.c_str(),
+ arraysize(expect_misc_info.BuildString));
+ c16lcpy(expect_misc_info.DbgBldStr,
+ debug_build_string_utf16.c_str(),
+ arraysize(expect_misc_info.DbgBldStr));
+
+ const timeval kStartTime =
+ { static_cast<time_t>(expect_misc_info.ProcessCreateTime), 0 };
+ const timeval kUserCPUTime =
+ { static_cast<time_t>(expect_misc_info.ProcessUserTime), 0 };
+ const timeval kSystemCPUTime =
+ { static_cast<time_t>(expect_misc_info.ProcessKernelTime), 0 };
+
+ TestProcessSnapshot process_snapshot;
+ process_snapshot.SetProcessID(expect_misc_info.ProcessId);
+ process_snapshot.SetProcessStartTime(kStartTime);
+ process_snapshot.SetProcessCPUTimes(kUserCPUTime, kSystemCPUTime);
+
+ auto system_snapshot = make_scoped_ptr(new TestSystemSnapshot());
+ const uint64_t kHzPerMHz = static_cast<uint64_t>(1E6);
+ system_snapshot->SetCPUFrequency(
+ expect_misc_info.ProcessorCurrentMhz * kHzPerMHz,
+ expect_misc_info.ProcessorMaxMhz * kHzPerMHz);
+ system_snapshot->SetTimeZone(SystemSnapshot::kObservingStandardTime,
+ expect_misc_info.TimeZone.Bias * -60,
+ (expect_misc_info.TimeZone.Bias +
+ expect_misc_info.TimeZone.DaylightBias) * -60,
+ kStandardTimeName,
+ kDaylightTimeName);
+ system_snapshot->SetOSVersionFull(kOSVersionFull);
+ system_snapshot->SetMachineDescription(kMachineDescription);
+
+ process_snapshot.SetSystem(system_snapshot.Pass());
+
+ auto misc_info_writer = make_scoped_ptr(new MinidumpMiscInfoWriter());
+ misc_info_writer->InitializeFromSnapshot(&process_snapshot);
+
+ MinidumpFileWriter minidump_file_writer;
+ minidump_file_writer.AddStream(misc_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_MISC_INFO_4* misc_info = nullptr;
+ ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &misc_info));
+
+ ExpectMiscInfoEqual(&expect_misc_info, misc_info);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer.cc
new file mode 100644
index 00000000000..8868badbd8a
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer.cc
@@ -0,0 +1,239 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_module_crashpad_info_writer.h"
+
+#include <sys/types.h>
+
+#include "base/logging.h"
+#include "minidump/minidump_simple_string_dictionary_writer.h"
+#include "snapshot/module_snapshot.h"
+#include "util/file/file_writer.h"
+#include "util/numeric/safe_assignment.h"
+
+namespace crashpad {
+
+MinidumpModuleCrashpadInfoWriter::MinidumpModuleCrashpadInfoWriter()
+ : MinidumpWritable(),
+ module_(),
+ list_annotations_(),
+ simple_annotations_() {
+ module_.version = MinidumpModuleCrashpadInfo::kVersion;
+}
+
+MinidumpModuleCrashpadInfoWriter::~MinidumpModuleCrashpadInfoWriter() {
+}
+
+void MinidumpModuleCrashpadInfoWriter::InitializeFromSnapshot(
+ const ModuleSnapshot* module_snapshot) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK(!list_annotations_);
+ DCHECK(!simple_annotations_);
+
+ auto list_annotations = make_scoped_ptr(new MinidumpUTF8StringListWriter());
+ list_annotations->InitializeFromVector(module_snapshot->AnnotationsVector());
+ if (list_annotations->IsUseful()) {
+ SetListAnnotations(list_annotations.Pass());
+ }
+
+ auto simple_annotations =
+ make_scoped_ptr(new MinidumpSimpleStringDictionaryWriter());
+ simple_annotations->InitializeFromMap(
+ module_snapshot->AnnotationsSimpleMap());
+ if (simple_annotations->IsUseful()) {
+ SetSimpleAnnotations(simple_annotations.Pass());
+ }
+}
+
+void MinidumpModuleCrashpadInfoWriter::SetListAnnotations(
+ scoped_ptr<MinidumpUTF8StringListWriter> list_annotations) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ list_annotations_ = list_annotations.Pass();
+}
+
+void MinidumpModuleCrashpadInfoWriter::SetSimpleAnnotations(
+ scoped_ptr<MinidumpSimpleStringDictionaryWriter> simple_annotations) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ simple_annotations_ = simple_annotations.Pass();
+}
+
+bool MinidumpModuleCrashpadInfoWriter::IsUseful() const {
+ return list_annotations_ || simple_annotations_;
+}
+
+bool MinidumpModuleCrashpadInfoWriter::Freeze() {
+ DCHECK_EQ(state(), kStateMutable);
+
+ if (!MinidumpWritable::Freeze()) {
+ return false;
+ }
+
+ if (list_annotations_) {
+ list_annotations_->RegisterLocationDescriptor(&module_.list_annotations);
+ }
+
+ if (simple_annotations_) {
+ simple_annotations_->RegisterLocationDescriptor(
+ &module_.simple_annotations);
+ }
+
+ return true;
+}
+
+size_t MinidumpModuleCrashpadInfoWriter::SizeOfObject() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ return sizeof(module_);
+}
+
+std::vector<internal::MinidumpWritable*>
+MinidumpModuleCrashpadInfoWriter::Children() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ std::vector<MinidumpWritable*> children;
+ if (list_annotations_) {
+ children.push_back(list_annotations_.get());
+ }
+ if (simple_annotations_) {
+ children.push_back(simple_annotations_.get());
+ }
+
+ return children;
+}
+
+bool MinidumpModuleCrashpadInfoWriter::WriteObject(
+ FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateWritable);
+
+ return file_writer->Write(&module_, sizeof(module_));
+}
+
+MinidumpModuleCrashpadInfoListWriter::MinidumpModuleCrashpadInfoListWriter()
+ : MinidumpWritable(),
+ module_crashpad_infos_(),
+ module_crashpad_info_links_(),
+ module_crashpad_info_list_base_() {
+}
+
+MinidumpModuleCrashpadInfoListWriter::~MinidumpModuleCrashpadInfoListWriter() {
+}
+
+void MinidumpModuleCrashpadInfoListWriter::InitializeFromSnapshot(
+ const std::vector<const ModuleSnapshot*>& module_snapshots) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK(module_crashpad_infos_.empty());
+ DCHECK(module_crashpad_info_links_.empty());
+
+ size_t count = module_snapshots.size();
+ for (size_t index = 0; index < count; ++index) {
+ const ModuleSnapshot* module_snapshot = module_snapshots[index];
+
+ auto module = make_scoped_ptr(new MinidumpModuleCrashpadInfoWriter());
+ module->InitializeFromSnapshot(module_snapshot);
+ if (module->IsUseful()) {
+ AddModule(module.Pass(), index);
+ }
+ }
+}
+
+void MinidumpModuleCrashpadInfoListWriter::AddModule(
+ scoped_ptr<MinidumpModuleCrashpadInfoWriter> module_crashpad_info,
+ size_t minidump_module_list_index) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size());
+
+ MinidumpModuleCrashpadInfoLink module_crashpad_info_link = {};
+ if (!AssignIfInRange(&module_crashpad_info_link.minidump_module_list_index,
+ minidump_module_list_index)) {
+ LOG(ERROR) << "minidump_module_list_index " << minidump_module_list_index
+ << " out of range";
+ return;
+ }
+
+ module_crashpad_info_links_.push_back(module_crashpad_info_link);
+ module_crashpad_infos_.push_back(module_crashpad_info.release());
+}
+
+bool MinidumpModuleCrashpadInfoListWriter::IsUseful() const {
+ DCHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size());
+ return !module_crashpad_infos_.empty();
+}
+
+bool MinidumpModuleCrashpadInfoListWriter::Freeze() {
+ DCHECK_EQ(state(), kStateMutable);
+ CHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size());
+
+ if (!MinidumpWritable::Freeze()) {
+ return false;
+ }
+
+ size_t module_count = module_crashpad_infos_.size();
+ if (!AssignIfInRange(&module_crashpad_info_list_base_.count, module_count)) {
+ LOG(ERROR) << "module_count " << module_count << " out of range";
+ return false;
+ }
+
+ for (size_t index = 0; index < module_count; ++index) {
+ module_crashpad_infos_[index]->RegisterLocationDescriptor(
+ &module_crashpad_info_links_[index].location);
+ }
+
+ return true;
+}
+
+size_t MinidumpModuleCrashpadInfoListWriter::SizeOfObject() {
+ DCHECK_GE(state(), kStateFrozen);
+ DCHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size());
+
+ return sizeof(module_crashpad_info_list_base_) +
+ module_crashpad_info_links_.size() *
+ sizeof(module_crashpad_info_links_[0]);
+}
+
+std::vector<internal::MinidumpWritable*>
+MinidumpModuleCrashpadInfoListWriter::Children() {
+ DCHECK_GE(state(), kStateFrozen);
+ DCHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size());
+
+ std::vector<MinidumpWritable*> children;
+ for (MinidumpModuleCrashpadInfoWriter* module : module_crashpad_infos_) {
+ children.push_back(module);
+ }
+
+ return children;
+}
+
+bool MinidumpModuleCrashpadInfoListWriter::WriteObject(
+ FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateWritable);
+ DCHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size());
+
+ WritableIoVec iov;
+ iov.iov_base = &module_crashpad_info_list_base_;
+ iov.iov_len = sizeof(module_crashpad_info_list_base_);
+ std::vector<WritableIoVec> iovecs(1, iov);
+
+ if (!module_crashpad_info_links_.empty()) {
+ iov.iov_base = &module_crashpad_info_links_[0];
+ iov.iov_len = module_crashpad_info_links_.size() *
+ sizeof(module_crashpad_info_links_[0]);
+ iovecs.push_back(iov);
+ }
+
+ return file_writer->WriteIoVec(&iovecs);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer.h
new file mode 100644
index 00000000000..601da7ee791
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer.h
@@ -0,0 +1,166 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_MINIDUMP_MINIDUMP_MODULE_CRASHPAD_INFO_WRITER_H_
+#define CRASHPAD_MINIDUMP_MINIDUMP_MODULE_CRASHPAD_INFO_WRITER_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "minidump/minidump_extensions.h"
+#include "minidump/minidump_string_writer.h"
+#include "minidump/minidump_writable.h"
+#include "util/stdlib/pointer_container.h"
+
+namespace crashpad {
+
+class MinidumpSimpleStringDictionaryWriter;
+class ModuleSnapshot;
+
+//! \brief The writer for a MinidumpModuleCrashpadInfo object in a minidump
+//! file.
+class MinidumpModuleCrashpadInfoWriter final
+ : public internal::MinidumpWritable {
+ public:
+ MinidumpModuleCrashpadInfoWriter();
+ ~MinidumpModuleCrashpadInfoWriter() override;
+
+ //! \brief Initializes MinidumpModuleCrashpadInfo based on \a module_snapshot.
+ //!
+ //! Only data in \a module_snapshot that is considered useful will be
+ //! included. For simple annotations, usefulness is determined by
+ //! MinidumpSimpleStringDictionaryWriter::IsUseful().
+ //!
+ //! \param[in] module_snapshot The module snapshot to use as source data.
+ //!
+ //! \note Valid in #kStateMutable. No mutator methods may be called before
+ //! this method, and it is not normally necessary to call any mutator
+ //! methods after this method.
+ void InitializeFromSnapshot(const ModuleSnapshot* module_snapshot);
+
+ //! \brief Arranges for MinidumpModuleCrashpadInfo::list_annotations to point
+ //! to the internal::MinidumpUTF8StringListWriter object to be written by
+ //! \a list_annotations.
+ //!
+ //! This object takes ownership of \a simple_annotations and becomes its
+ //! parent in the overall tree of internal::MinidumpWritable objects.
+ //!
+ //! \note Valid in #kStateMutable.
+ void SetListAnnotations(
+ scoped_ptr<MinidumpUTF8StringListWriter> list_annotations);
+
+ //! \brief Arranges for MinidumpModuleCrashpadInfo::simple_annotations to
+ //! point to the MinidumpSimpleStringDictionaryWriter object to be written
+ //! by \a simple_annotations.
+ //!
+ //! This object takes ownership of \a simple_annotations and becomes its
+ //! parent in the overall tree of internal::MinidumpWritable objects.
+ //!
+ //! \note Valid in #kStateMutable.
+ void SetSimpleAnnotations(
+ scoped_ptr<MinidumpSimpleStringDictionaryWriter> simple_annotations);
+
+ //! \brief Determines whether the object is useful.
+ //!
+ //! A useful object is one that carries data that makes a meaningful
+ //! contribution to a minidump file. An object carrying list annotations or
+ //! simple annotations would be considered useful.
+ //!
+ //! \return `true` if the object is useful, `false` otherwise.
+ bool IsUseful() const;
+
+ protected:
+ // MinidumpWritable:
+ bool Freeze() override;
+ size_t SizeOfObject() override;
+ std::vector<MinidumpWritable*> Children() override;
+ bool WriteObject(FileWriterInterface* file_writer) override;
+
+ private:
+ MinidumpModuleCrashpadInfo module_;
+ scoped_ptr<MinidumpUTF8StringListWriter> list_annotations_;
+ scoped_ptr<MinidumpSimpleStringDictionaryWriter> simple_annotations_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCrashpadInfoWriter);
+};
+
+//! \brief The writer for a MinidumpModuleCrashpadInfoList object in a minidump
+//! file, containing a list of MinidumpModuleCrashpadInfo objects.
+class MinidumpModuleCrashpadInfoListWriter final
+ : public internal::MinidumpWritable {
+ public:
+ MinidumpModuleCrashpadInfoListWriter();
+ ~MinidumpModuleCrashpadInfoListWriter() override;
+
+ //! \brief Adds an initialized MinidumpModuleCrashpadInfo for modules in \a
+ //! module_snapshots to the MinidumpModuleCrashpadInfoList.
+ //!
+ //! Only modules in \a module_snapshots that would produce a useful
+ //! MinidumpModuleCrashpadInfo structure are included. Usefulness is
+ //! determined by MinidumpModuleCrashpadInfoWriter::IsUseful().
+ //!
+ //! \param[in] module_snapshots The module snapshots to use as source data.
+ //!
+ //! \note Valid in #kStateMutable. AddModule() may not be called before this
+ //! method, and it is not normally necessary to call AddModule() after
+ //! this method.
+ void InitializeFromSnapshot(
+ const std::vector<const ModuleSnapshot*>& module_snapshots);
+
+ //! \brief Adds a MinidumpModuleCrashpadInfo to the
+ //! MinidumpModuleCrashpadInfoList.
+ //!
+ //! \param[in] module Extended Crashpad-specific information about the module.
+ //! This object takes ownership of \a module and becomes its parent in the
+ //! overall tree of internal::MinidumpWritable objects.
+ //! \param[in] module_list_index The index of the MINIDUMP_MODULE in the
+ //! minidump file’s MINIDUMP_MODULE_LIST stream that corresponds to \a
+ //! module_crashpad_info.
+ //!
+ //! \note Valid in #kStateMutable.
+ void AddModule(
+ scoped_ptr<MinidumpModuleCrashpadInfoWriter> module_crashpad_info,
+ size_t minidump_module_list_index);
+
+ //! \brief Determines whether the object is useful.
+ //!
+ //! A useful object is one that carries data that makes a meaningful
+ //! contribution to a minidump file. An object carrying children would be
+ //! considered useful.
+ //!
+ //! \return `true` if the object is useful, `false` otherwise.
+ bool IsUseful() const;
+
+ protected:
+ // MinidumpWritable:
+ bool Freeze() override;
+ size_t SizeOfObject() override;
+ std::vector<MinidumpWritable*> Children() override;
+ bool WriteObject(FileWriterInterface* file_writer) override;
+
+ private:
+ PointerVector<MinidumpModuleCrashpadInfoWriter> module_crashpad_infos_;
+ std::vector<MinidumpModuleCrashpadInfoLink> module_crashpad_info_links_;
+ MinidumpModuleCrashpadInfoList module_crashpad_info_list_base_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCrashpadInfoListWriter);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_MINIDUMP_MINIDUMP_MODULE_CRASHPAD_INFO_WRITER_H_
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer_test.cc
new file mode 100644
index 00000000000..458913fe118
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer_test.cc
@@ -0,0 +1,471 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_module_crashpad_info_writer.h"
+
+#include <windows.h>
+#include <dbghelp.h>
+
+#include "gtest/gtest.h"
+#include "minidump/minidump_extensions.h"
+#include "minidump/minidump_simple_string_dictionary_writer.h"
+#include "minidump/test/minidump_file_writer_test_util.h"
+#include "minidump/test/minidump_string_writer_test_util.h"
+#include "minidump/test/minidump_writable_test_util.h"
+#include "snapshot/test/test_module_snapshot.h"
+#include "util/file/string_file.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+const MinidumpModuleCrashpadInfoList* MinidumpModuleCrashpadInfoListAtStart(
+ const std::string& file_contents,
+ size_t count) {
+ MINIDUMP_LOCATION_DESCRIPTOR location_descriptor;
+ location_descriptor.DataSize =
+ static_cast<uint32_t>(sizeof(MinidumpModuleCrashpadInfoList) +
+ count * sizeof(MinidumpModuleCrashpadInfoLink));
+ location_descriptor.Rva = 0;
+
+ const MinidumpModuleCrashpadInfoList* list =
+ MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfoList>(
+ file_contents, location_descriptor);
+ if (!list) {
+ return nullptr;
+ }
+
+ if (list->count != count) {
+ EXPECT_EQ(count, list->count);
+ return nullptr;
+ }
+
+ return list;
+}
+
+TEST(MinidumpModuleCrashpadInfoWriter, EmptyList) {
+ StringFile string_file;
+
+ auto module_list_writer =
+ make_scoped_ptr(new MinidumpModuleCrashpadInfoListWriter());
+ EXPECT_FALSE(module_list_writer->IsUseful());
+
+ EXPECT_TRUE(module_list_writer->WriteEverything(&string_file));
+ ASSERT_EQ(sizeof(MinidumpModuleCrashpadInfoList),
+ string_file.string().size());
+
+ const MinidumpModuleCrashpadInfoList* module_list =
+ MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 0);
+ ASSERT_TRUE(module_list);
+}
+
+TEST(MinidumpModuleCrashpadInfoWriter, EmptyModule) {
+ StringFile string_file;
+
+ auto module_list_writer =
+ make_scoped_ptr(new MinidumpModuleCrashpadInfoListWriter());
+ auto module_writer = make_scoped_ptr(new MinidumpModuleCrashpadInfoWriter());
+ EXPECT_FALSE(module_writer->IsUseful());
+ module_list_writer->AddModule(module_writer.Pass(), 0);
+
+ EXPECT_TRUE(module_list_writer->IsUseful());
+
+ EXPECT_TRUE(module_list_writer->WriteEverything(&string_file));
+ ASSERT_EQ(sizeof(MinidumpModuleCrashpadInfoList) +
+ sizeof(MinidumpModuleCrashpadInfoLink) +
+ sizeof(MinidumpModuleCrashpadInfo),
+ string_file.string().size());
+
+ const MinidumpModuleCrashpadInfoList* module_list =
+ MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 1);
+ ASSERT_TRUE(module_list);
+
+ EXPECT_EQ(0u, module_list->modules[0].minidump_module_list_index);
+ const MinidumpModuleCrashpadInfo* module =
+ MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>(
+ string_file.string(), module_list->modules[0].location);
+ ASSERT_TRUE(module);
+
+ EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module->version);
+ EXPECT_EQ(0u, module->list_annotations.DataSize);
+ EXPECT_EQ(0u, module->list_annotations.Rva);
+ EXPECT_EQ(0u, module->simple_annotations.DataSize);
+ EXPECT_EQ(0u, module->simple_annotations.Rva);
+}
+
+TEST(MinidumpModuleCrashpadInfoWriter, FullModule) {
+ const uint32_t kMinidumpModuleListIndex = 1;
+ const char kKey[] = "key";
+ const char kValue[] = "value";
+ const char kEntry[] = "entry";
+ std::vector<std::string> vector(1, std::string(kEntry));
+
+ StringFile string_file;
+
+ auto module_list_writer =
+ make_scoped_ptr(new MinidumpModuleCrashpadInfoListWriter());
+
+ auto module_writer = make_scoped_ptr(new MinidumpModuleCrashpadInfoWriter());
+ auto string_list_writer = make_scoped_ptr(new MinidumpUTF8StringListWriter());
+ string_list_writer->InitializeFromVector(vector);
+ module_writer->SetListAnnotations(string_list_writer.Pass());
+ auto simple_string_dictionary_writer =
+ make_scoped_ptr(new MinidumpSimpleStringDictionaryWriter());
+ auto simple_string_dictionary_entry_writer =
+ make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter());
+ simple_string_dictionary_entry_writer->SetKeyValue(kKey, kValue);
+ simple_string_dictionary_writer->AddEntry(
+ simple_string_dictionary_entry_writer.Pass());
+ module_writer->SetSimpleAnnotations(simple_string_dictionary_writer.Pass());
+ EXPECT_TRUE(module_writer->IsUseful());
+ module_list_writer->AddModule(module_writer.Pass(), kMinidumpModuleListIndex);
+
+ EXPECT_TRUE(module_list_writer->IsUseful());
+
+ EXPECT_TRUE(module_list_writer->WriteEverything(&string_file));
+ ASSERT_EQ(sizeof(MinidumpModuleCrashpadInfoList) +
+ sizeof(MinidumpModuleCrashpadInfoLink) +
+ sizeof(MinidumpModuleCrashpadInfo) +
+ sizeof(MinidumpRVAList) +
+ sizeof(RVA) +
+ sizeof(MinidumpSimpleStringDictionary) +
+ sizeof(MinidumpSimpleStringDictionaryEntry) +
+ sizeof(MinidumpUTF8String) + arraysize(kEntry) + 2 + // padding
+ sizeof(MinidumpUTF8String) + arraysize(kKey) +
+ sizeof(MinidumpUTF8String) + arraysize(kValue),
+ string_file.string().size());
+
+ const MinidumpModuleCrashpadInfoList* module_list =
+ MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 1);
+ ASSERT_TRUE(module_list);
+
+ EXPECT_EQ(kMinidumpModuleListIndex,
+ module_list->modules[0].minidump_module_list_index);
+ const MinidumpModuleCrashpadInfo* module =
+ MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>(
+ string_file.string(), module_list->modules[0].location);
+ ASSERT_TRUE(module);
+
+ EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module->version);
+ EXPECT_NE(0u, module->list_annotations.DataSize);
+ EXPECT_NE(0u, module->list_annotations.Rva);
+ EXPECT_NE(0u, module->simple_annotations.DataSize);
+ EXPECT_NE(0u, module->simple_annotations.Rva);
+
+ const MinidumpRVAList* list_annotations =
+ MinidumpWritableAtLocationDescriptor<MinidumpRVAList>(
+ string_file.string(), module->list_annotations);
+ ASSERT_TRUE(list_annotations);
+
+ ASSERT_EQ(1u, list_annotations->count);
+ EXPECT_EQ(kEntry,
+ MinidumpUTF8StringAtRVAAsString(string_file.string(),
+ list_annotations->children[0]));
+
+ const MinidumpSimpleStringDictionary* simple_annotations =
+ MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(
+ string_file.string(), module->simple_annotations);
+ ASSERT_TRUE(simple_annotations);
+
+ ASSERT_EQ(1u, simple_annotations->count);
+ EXPECT_EQ(kKey,
+ MinidumpUTF8StringAtRVAAsString(
+ string_file.string(), simple_annotations->entries[0].key));
+ EXPECT_EQ(kValue,
+ MinidumpUTF8StringAtRVAAsString(
+ string_file.string(), simple_annotations->entries[0].value));
+}
+
+TEST(MinidumpModuleCrashpadInfoWriter, ThreeModules) {
+ const uint32_t kMinidumpModuleListIndex0 = 0;
+ const char kKey0[] = "key";
+ const char kValue0[] = "value";
+ const uint32_t kMinidumpModuleListIndex1 = 2;
+ const uint32_t kMinidumpModuleListIndex2 = 5;
+ const char kKey2A[] = "K";
+ const char kValue2A[] = "VVV";
+ const char kKey2B[] = "river";
+ const char kValue2B[] = "hudson";
+
+ StringFile string_file;
+
+ auto module_list_writer =
+ make_scoped_ptr(new MinidumpModuleCrashpadInfoListWriter());
+
+ auto module_writer_0 =
+ make_scoped_ptr(new MinidumpModuleCrashpadInfoWriter());
+ auto simple_string_dictionary_writer_0 =
+ make_scoped_ptr(new MinidumpSimpleStringDictionaryWriter());
+ auto simple_string_dictionary_entry_writer_0 =
+ make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter());
+ simple_string_dictionary_entry_writer_0->SetKeyValue(kKey0, kValue0);
+ simple_string_dictionary_writer_0->AddEntry(
+ simple_string_dictionary_entry_writer_0.Pass());
+ module_writer_0->SetSimpleAnnotations(
+ simple_string_dictionary_writer_0.Pass());
+ EXPECT_TRUE(module_writer_0->IsUseful());
+ module_list_writer->AddModule(module_writer_0.Pass(),
+ kMinidumpModuleListIndex0);
+
+ auto module_writer_1 =
+ make_scoped_ptr(new MinidumpModuleCrashpadInfoWriter());
+ EXPECT_FALSE(module_writer_1->IsUseful());
+ module_list_writer->AddModule(module_writer_1.Pass(),
+ kMinidumpModuleListIndex1);
+
+ auto module_writer_2 =
+ make_scoped_ptr(new MinidumpModuleCrashpadInfoWriter());
+ auto simple_string_dictionary_writer_2 =
+ make_scoped_ptr(new MinidumpSimpleStringDictionaryWriter());
+ auto simple_string_dictionary_entry_writer_2a =
+ make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter());
+ simple_string_dictionary_entry_writer_2a->SetKeyValue(kKey2A, kValue2A);
+ simple_string_dictionary_writer_2->AddEntry(
+ simple_string_dictionary_entry_writer_2a.Pass());
+ auto simple_string_dictionary_entry_writer_2b =
+ make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter());
+ simple_string_dictionary_entry_writer_2b->SetKeyValue(kKey2B, kValue2B);
+ simple_string_dictionary_writer_2->AddEntry(
+ simple_string_dictionary_entry_writer_2b.Pass());
+ module_writer_2->SetSimpleAnnotations(
+ simple_string_dictionary_writer_2.Pass());
+ EXPECT_TRUE(module_writer_2->IsUseful());
+ module_list_writer->AddModule(module_writer_2.Pass(),
+ kMinidumpModuleListIndex2);
+
+ EXPECT_TRUE(module_list_writer->IsUseful());
+
+ EXPECT_TRUE(module_list_writer->WriteEverything(&string_file));
+
+ const MinidumpModuleCrashpadInfoList* module_list =
+ MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 3);
+ ASSERT_TRUE(module_list);
+
+ EXPECT_EQ(kMinidumpModuleListIndex0,
+ module_list->modules[0].minidump_module_list_index);
+ const MinidumpModuleCrashpadInfo* module_0 =
+ MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>(
+ string_file.string(), module_list->modules[0].location);
+ ASSERT_TRUE(module_0);
+
+ EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module_0->version);
+
+ const MinidumpRVAList* list_annotations_0 =
+ MinidumpWritableAtLocationDescriptor<MinidumpRVAList>(
+ string_file.string(), module_0->list_annotations);
+ EXPECT_FALSE(list_annotations_0);
+
+ const MinidumpSimpleStringDictionary* simple_annotations_0 =
+ MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(
+ string_file.string(), module_0->simple_annotations);
+ ASSERT_TRUE(simple_annotations_0);
+
+ ASSERT_EQ(1u, simple_annotations_0->count);
+ EXPECT_EQ(kKey0,
+ MinidumpUTF8StringAtRVAAsString(
+ string_file.string(), simple_annotations_0->entries[0].key));
+ EXPECT_EQ(kValue0,
+ MinidumpUTF8StringAtRVAAsString(
+ string_file.string(), simple_annotations_0->entries[0].value));
+
+ EXPECT_EQ(kMinidumpModuleListIndex1,
+ module_list->modules[1].minidump_module_list_index);
+ const MinidumpModuleCrashpadInfo* module_1 =
+ MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>(
+ string_file.string(), module_list->modules[1].location);
+ ASSERT_TRUE(module_1);
+
+ EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module_1->version);
+
+ const MinidumpRVAList* list_annotations_1 =
+ MinidumpWritableAtLocationDescriptor<MinidumpRVAList>(
+ string_file.string(), module_1->list_annotations);
+ EXPECT_FALSE(list_annotations_1);
+
+ const MinidumpSimpleStringDictionary* simple_annotations_1 =
+ MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(
+ string_file.string(), module_1->simple_annotations);
+ EXPECT_FALSE(simple_annotations_1);
+
+ EXPECT_EQ(kMinidumpModuleListIndex2,
+ module_list->modules[2].minidump_module_list_index);
+ const MinidumpModuleCrashpadInfo* module_2 =
+ MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>(
+ string_file.string(), module_list->modules[2].location);
+ ASSERT_TRUE(module_2);
+
+ EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module_2->version);
+
+ const MinidumpRVAList* list_annotations_2 =
+ MinidumpWritableAtLocationDescriptor<MinidumpRVAList>(
+ string_file.string(), module_2->list_annotations);
+ EXPECT_FALSE(list_annotations_2);
+
+ const MinidumpSimpleStringDictionary* simple_annotations_2 =
+ MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(
+ string_file.string(), module_2->simple_annotations);
+ ASSERT_TRUE(simple_annotations_2);
+
+ ASSERT_EQ(2u, simple_annotations_2->count);
+ EXPECT_EQ(kKey2A,
+ MinidumpUTF8StringAtRVAAsString(
+ string_file.string(), simple_annotations_2->entries[0].key));
+ EXPECT_EQ(kValue2A,
+ MinidumpUTF8StringAtRVAAsString(
+ string_file.string(), simple_annotations_2->entries[0].value));
+ EXPECT_EQ(kKey2B,
+ MinidumpUTF8StringAtRVAAsString(
+ string_file.string(), simple_annotations_2->entries[1].key));
+ EXPECT_EQ(kValue2B,
+ MinidumpUTF8StringAtRVAAsString(
+ string_file.string(), simple_annotations_2->entries[1].value));
+}
+
+TEST(MinidumpModuleCrashpadInfoWriter, InitializeFromSnapshot) {
+ const char kKey0A[] = "k";
+ const char kValue0A[] = "value";
+ const char kKey0B[] = "hudson";
+ const char kValue0B[] = "estuary";
+ const char kKey2[] = "k";
+ const char kValue2[] = "different_value";
+ const char kEntry3A[] = "list";
+ const char kEntry3B[] = "erine";
+
+ std::vector<const ModuleSnapshot*> module_snapshots;
+
+ TestModuleSnapshot module_snapshot_0;
+ std::map<std::string, std::string> annotations_simple_map_0;
+ annotations_simple_map_0[kKey0A] = kValue0A;
+ annotations_simple_map_0[kKey0B] = kValue0B;
+ module_snapshot_0.SetAnnotationsSimpleMap(annotations_simple_map_0);
+ module_snapshots.push_back(&module_snapshot_0);
+
+ // module_snapshot_1 is not expected to be written because it would not carry
+ // any MinidumpModuleCrashpadInfo data.
+ TestModuleSnapshot module_snapshot_1;
+ module_snapshots.push_back(&module_snapshot_1);
+
+ TestModuleSnapshot module_snapshot_2;
+ std::map<std::string, std::string> annotations_simple_map_2;
+ annotations_simple_map_2[kKey2] = kValue2;
+ module_snapshot_2.SetAnnotationsSimpleMap(annotations_simple_map_2);
+ module_snapshots.push_back(&module_snapshot_2);
+
+ TestModuleSnapshot module_snapshot_3;
+ std::vector<std::string> annotations_vector_3;
+ annotations_vector_3.push_back(kEntry3A);
+ annotations_vector_3.push_back(kEntry3B);
+ module_snapshot_3.SetAnnotationsVector(annotations_vector_3);
+ module_snapshots.push_back(&module_snapshot_3);
+
+ auto module_list_writer =
+ make_scoped_ptr(new MinidumpModuleCrashpadInfoListWriter());
+ module_list_writer->InitializeFromSnapshot(module_snapshots);
+ EXPECT_TRUE(module_list_writer->IsUseful());
+
+ StringFile string_file;
+ ASSERT_TRUE(module_list_writer->WriteEverything(&string_file));
+
+ const MinidumpModuleCrashpadInfoList* module_list =
+ MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 3);
+ ASSERT_TRUE(module_list);
+
+ EXPECT_EQ(0u, module_list->modules[0].minidump_module_list_index);
+ const MinidumpModuleCrashpadInfo* module_0 =
+ MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>(
+ string_file.string(), module_list->modules[0].location);
+ ASSERT_TRUE(module_0);
+
+ EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module_0->version);
+
+ const MinidumpRVAList* list_annotations_0 =
+ MinidumpWritableAtLocationDescriptor<MinidumpRVAList>(
+ string_file.string(), module_0->list_annotations);
+ EXPECT_FALSE(list_annotations_0);
+
+ const MinidumpSimpleStringDictionary* simple_annotations_0 =
+ MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(
+ string_file.string(), module_0->simple_annotations);
+ ASSERT_TRUE(simple_annotations_0);
+
+ ASSERT_EQ(annotations_simple_map_0.size(), simple_annotations_0->count);
+ EXPECT_EQ(kKey0B,
+ MinidumpUTF8StringAtRVAAsString(
+ string_file.string(), simple_annotations_0->entries[0].key));
+ EXPECT_EQ(kValue0B,
+ MinidumpUTF8StringAtRVAAsString(
+ string_file.string(), simple_annotations_0->entries[0].value));
+ EXPECT_EQ(kKey0A,
+ MinidumpUTF8StringAtRVAAsString(
+ string_file.string(), simple_annotations_0->entries[1].key));
+ EXPECT_EQ(kValue0A,
+ MinidumpUTF8StringAtRVAAsString(
+ string_file.string(), simple_annotations_0->entries[1].value));
+
+ EXPECT_EQ(2u, module_list->modules[1].minidump_module_list_index);
+ const MinidumpModuleCrashpadInfo* module_2 =
+ MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>(
+ string_file.string(), module_list->modules[1].location);
+ ASSERT_TRUE(module_2);
+
+ EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module_2->version);
+
+ const MinidumpRVAList* list_annotations_2 =
+ MinidumpWritableAtLocationDescriptor<MinidumpRVAList>(
+ string_file.string(), module_2->list_annotations);
+ EXPECT_FALSE(list_annotations_2);
+
+ const MinidumpSimpleStringDictionary* simple_annotations_2 =
+ MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(
+ string_file.string(), module_2->simple_annotations);
+ ASSERT_TRUE(simple_annotations_2);
+
+ ASSERT_EQ(annotations_simple_map_2.size(), simple_annotations_2->count);
+ EXPECT_EQ(kKey2,
+ MinidumpUTF8StringAtRVAAsString(
+ string_file.string(), simple_annotations_2->entries[0].key));
+ EXPECT_EQ(kValue2,
+ MinidumpUTF8StringAtRVAAsString(
+ string_file.string(), simple_annotations_2->entries[0].value));
+
+ EXPECT_EQ(3u, module_list->modules[2].minidump_module_list_index);
+ const MinidumpModuleCrashpadInfo* module_3 =
+ MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>(
+ string_file.string(), module_list->modules[2].location);
+ ASSERT_TRUE(module_3);
+
+ EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module_3->version);
+
+ const MinidumpRVAList* list_annotations_3 =
+ MinidumpWritableAtLocationDescriptor<MinidumpRVAList>(
+ string_file.string(), module_3->list_annotations);
+ ASSERT_TRUE(list_annotations_3);
+
+ ASSERT_EQ(annotations_vector_3.size(), list_annotations_3->count);
+ EXPECT_EQ(kEntry3A,
+ MinidumpUTF8StringAtRVAAsString(string_file.string(),
+ list_annotations_3->children[0]));
+ EXPECT_EQ(kEntry3B,
+ MinidumpUTF8StringAtRVAAsString(string_file.string(),
+ list_annotations_3->children[1]));
+
+ const MinidumpSimpleStringDictionary* simple_annotations_3 =
+ MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(
+ string_file.string(), module_3->simple_annotations);
+ EXPECT_FALSE(simple_annotations_3);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_module_writer.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_module_writer.cc
new file mode 100644
index 00000000000..dc5eddfa6e6
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_module_writer.cc
@@ -0,0 +1,459 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_module_writer.h"
+
+#include <sys/types.h>
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "minidump/minidump_string_writer.h"
+#include "minidump/minidump_writer_util.h"
+#include "snapshot/module_snapshot.h"
+#include "util/file/file_writer.h"
+#include "util/numeric/in_range_cast.h"
+#include "util/numeric/safe_assignment.h"
+
+namespace crashpad {
+
+MinidumpModuleCodeViewRecordWriter::~MinidumpModuleCodeViewRecordWriter() {
+}
+
+namespace internal {
+
+template <typename CodeViewRecordType>
+MinidumpModuleCodeViewRecordPDBLinkWriter<
+ CodeViewRecordType>::MinidumpModuleCodeViewRecordPDBLinkWriter()
+ : MinidumpModuleCodeViewRecordWriter(), codeview_record_(), pdb_name_() {
+ codeview_record_.signature = CodeViewRecordType::kSignature;
+}
+
+template <typename CodeViewRecordType>
+MinidumpModuleCodeViewRecordPDBLinkWriter<
+ CodeViewRecordType>::~MinidumpModuleCodeViewRecordPDBLinkWriter() {
+}
+
+template <typename CodeViewRecordType>
+size_t
+MinidumpModuleCodeViewRecordPDBLinkWriter<CodeViewRecordType>::SizeOfObject() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ // NUL-terminate.
+ return offsetof(decltype(codeview_record_), pdb_name) +
+ (pdb_name_.size() + 1) * sizeof(pdb_name_[0]);
+}
+
+template <typename CodeViewRecordType>
+bool MinidumpModuleCodeViewRecordPDBLinkWriter<CodeViewRecordType>::WriteObject(
+ FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateWritable);
+
+ WritableIoVec iov;
+ iov.iov_base = &codeview_record_;
+ iov.iov_len = offsetof(decltype(codeview_record_), pdb_name);
+ std::vector<WritableIoVec> iovecs(1, iov);
+
+ // NUL-terminate.
+ iov.iov_base = &pdb_name_[0];
+ iov.iov_len = (pdb_name_.size() + 1) * sizeof(pdb_name_[0]);
+ iovecs.push_back(iov);
+
+ return file_writer->WriteIoVec(&iovecs);
+}
+
+} // namespace internal
+
+template class internal::MinidumpModuleCodeViewRecordPDBLinkWriter<
+ MinidumpModuleCodeViewRecordPDB20>;
+
+MinidumpModuleCodeViewRecordPDB20Writer::
+ ~MinidumpModuleCodeViewRecordPDB20Writer() {
+}
+
+void MinidumpModuleCodeViewRecordPDB20Writer::SetTimestampAndAge(
+ time_t timestamp,
+ uint32_t age) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ internal::MinidumpWriterUtil::AssignTimeT(&codeview_record()->timestamp,
+ timestamp);
+
+ codeview_record()->age = age;
+}
+
+template class internal::MinidumpModuleCodeViewRecordPDBLinkWriter<
+ MinidumpModuleCodeViewRecordPDB70>;
+
+MinidumpModuleCodeViewRecordPDB70Writer::
+ ~MinidumpModuleCodeViewRecordPDB70Writer() {
+}
+
+void MinidumpModuleCodeViewRecordPDB70Writer::InitializeFromSnapshot(
+ const ModuleSnapshot* module_snapshot) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ std::string name = module_snapshot->Name();
+ std::string leaf_name;
+ size_t last_slash = name.find_last_of('/');
+ if (last_slash != std::string::npos) {
+ leaf_name = name.substr(last_slash + 1);
+ } else {
+ leaf_name = name;
+ }
+ SetPDBName(leaf_name);
+
+ UUID uuid;
+ module_snapshot->UUID(&uuid);
+ SetUUIDAndAge(uuid, 0);
+}
+
+MinidumpModuleMiscDebugRecordWriter::MinidumpModuleMiscDebugRecordWriter()
+ : internal::MinidumpWritable(),
+ image_debug_misc_(),
+ data_(),
+ data_utf16_() {
+}
+
+MinidumpModuleMiscDebugRecordWriter::~MinidumpModuleMiscDebugRecordWriter() {
+}
+
+void MinidumpModuleMiscDebugRecordWriter::SetData(const std::string& data,
+ bool utf16) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ if (!utf16) {
+ data_utf16_.clear();
+ image_debug_misc_.Unicode = 0;
+ data_ = data;
+ } else {
+ data_.clear();
+ image_debug_misc_.Unicode = 1;
+ data_utf16_ = internal::MinidumpWriterUtil::ConvertUTF8ToUTF16(data);
+ }
+}
+
+bool MinidumpModuleMiscDebugRecordWriter::Freeze() {
+ DCHECK_EQ(state(), kStateMutable);
+
+ if (!MinidumpWritable::Freeze()) {
+ return false;
+ }
+
+ // NUL-terminate.
+ if (!image_debug_misc_.Unicode) {
+ DCHECK(data_utf16_.empty());
+ image_debug_misc_.Length = base::checked_cast<uint32_t>(
+ offsetof(decltype(image_debug_misc_), Data) +
+ (data_.size() + 1) * sizeof(data_[0]));
+ } else {
+ DCHECK(data_.empty());
+ image_debug_misc_.Length = base::checked_cast<uint32_t>(
+ offsetof(decltype(image_debug_misc_), Data) +
+ (data_utf16_.size() + 1) * sizeof(data_utf16_[0]));
+ }
+
+ return true;
+}
+
+size_t MinidumpModuleMiscDebugRecordWriter::SizeOfObject() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ return image_debug_misc_.Length;
+}
+
+bool MinidumpModuleMiscDebugRecordWriter::WriteObject(
+ FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateWritable);
+
+ const size_t base_length = offsetof(decltype(image_debug_misc_), Data);
+
+ WritableIoVec iov;
+ iov.iov_base = &image_debug_misc_;
+ iov.iov_len = base_length;
+ std::vector<WritableIoVec> iovecs(1, iov);
+
+ if (!image_debug_misc_.Unicode) {
+ DCHECK(data_utf16_.empty());
+ iov.iov_base = &data_[0];
+ } else {
+ DCHECK(data_.empty());
+ iov.iov_base = &data_utf16_[0];
+ }
+ iov.iov_len = image_debug_misc_.Length - base_length;
+ iovecs.push_back(iov);
+
+ return file_writer->WriteIoVec(&iovecs);
+}
+
+MinidumpModuleWriter::MinidumpModuleWriter()
+ : MinidumpWritable(),
+ module_(),
+ name_(),
+ codeview_record_(nullptr),
+ misc_debug_record_(nullptr) {
+ module_.VersionInfo.dwSignature = VS_FFI_SIGNATURE;
+ module_.VersionInfo.dwStrucVersion = VS_FFI_STRUCVERSION;
+}
+
+MinidumpModuleWriter::~MinidumpModuleWriter() {
+}
+
+void MinidumpModuleWriter::InitializeFromSnapshot(
+ const ModuleSnapshot* module_snapshot) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK(!name_);
+ DCHECK(!codeview_record_);
+ DCHECK(!misc_debug_record_);
+
+ SetName(module_snapshot->Name());
+
+ SetImageBaseAddress(module_snapshot->Address());
+ SetImageSize(InRangeCast<uint32_t>(module_snapshot->Size(),
+ std::numeric_limits<uint32_t>::max()));
+ SetTimestamp(module_snapshot->Timestamp());
+
+ uint16_t v[4];
+ module_snapshot->FileVersion(&v[0], &v[1], &v[2], &v[3]);
+ SetFileVersion(v[0], v[1], v[2], v[3]);
+
+ module_snapshot->SourceVersion(&v[0], &v[1], &v[2], &v[3]);
+ SetProductVersion(v[0], v[1], v[2], v[3]);
+
+ uint32_t file_type;
+ switch (module_snapshot->GetModuleType()) {
+ case ModuleSnapshot::kModuleTypeExecutable:
+ file_type = VFT_APP;
+ break;
+ case ModuleSnapshot::kModuleTypeSharedLibrary:
+ case ModuleSnapshot::kModuleTypeLoadableModule:
+ file_type = VFT_DLL;
+ break;
+ default:
+ file_type = VFT_UNKNOWN;
+ break;
+ }
+ SetFileTypeAndSubtype(file_type, VFT2_UNKNOWN);
+
+ auto codeview_record =
+ make_scoped_ptr(new MinidumpModuleCodeViewRecordPDB70Writer());
+ codeview_record->InitializeFromSnapshot(module_snapshot);
+ SetCodeViewRecord(codeview_record.Pass());
+}
+
+const MINIDUMP_MODULE* MinidumpModuleWriter::MinidumpModule() const {
+ DCHECK_EQ(state(), kStateWritable);
+
+ return &module_;
+}
+
+void MinidumpModuleWriter::SetName(const std::string& name) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ if (!name_) {
+ name_.reset(new internal::MinidumpUTF16StringWriter());
+ }
+ name_->SetUTF8(name);
+}
+
+void MinidumpModuleWriter::SetCodeViewRecord(
+ scoped_ptr<MinidumpModuleCodeViewRecordWriter> codeview_record) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ codeview_record_ = codeview_record.Pass();
+}
+
+void MinidumpModuleWriter::SetMiscDebugRecord(
+ scoped_ptr<MinidumpModuleMiscDebugRecordWriter> misc_debug_record) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ misc_debug_record_ = misc_debug_record.Pass();
+}
+
+void MinidumpModuleWriter::SetTimestamp(time_t timestamp) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ internal::MinidumpWriterUtil::AssignTimeT(&module_.TimeDateStamp, timestamp);
+}
+
+void MinidumpModuleWriter::SetFileVersion(uint16_t version_0,
+ uint16_t version_1,
+ uint16_t version_2,
+ uint16_t version_3) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ module_.VersionInfo.dwFileVersionMS =
+ (implicit_cast<uint32_t>(version_0) << 16) | version_1;
+ module_.VersionInfo.dwFileVersionLS =
+ (implicit_cast<uint32_t>(version_2) << 16) | version_3;
+}
+
+void MinidumpModuleWriter::SetProductVersion(uint16_t version_0,
+ uint16_t version_1,
+ uint16_t version_2,
+ uint16_t version_3) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ module_.VersionInfo.dwProductVersionMS =
+ (implicit_cast<uint32_t>(version_0) << 16) | version_1;
+ module_.VersionInfo.dwProductVersionLS =
+ (implicit_cast<uint32_t>(version_2) << 16) | version_3;
+}
+
+void MinidumpModuleWriter::SetFileFlagsAndMask(uint32_t file_flags,
+ uint32_t file_flags_mask) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK_EQ(file_flags & ~file_flags_mask, 0u);
+
+ module_.VersionInfo.dwFileFlags = file_flags;
+ module_.VersionInfo.dwFileFlagsMask = file_flags_mask;
+}
+
+bool MinidumpModuleWriter::Freeze() {
+ DCHECK_EQ(state(), kStateMutable);
+ CHECK(name_);
+
+ if (!MinidumpWritable::Freeze()) {
+ return false;
+ }
+
+ name_->RegisterRVA(&module_.ModuleNameRva);
+
+ if (codeview_record_) {
+ codeview_record_->RegisterLocationDescriptor(&module_.CvRecord);
+ }
+
+ if (misc_debug_record_) {
+ misc_debug_record_->RegisterLocationDescriptor(&module_.MiscRecord);
+ }
+
+ return true;
+}
+
+size_t MinidumpModuleWriter::SizeOfObject() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ // This object doesn’t directly write anything itself. Its MINIDUMP_MODULE is
+ // written by its parent as part of a MINIDUMP_MODULE_LIST, and its children
+ // are responsible for writing themselves.
+ return 0;
+}
+
+std::vector<internal::MinidumpWritable*> MinidumpModuleWriter::Children() {
+ DCHECK_GE(state(), kStateFrozen);
+ DCHECK(name_);
+
+ std::vector<MinidumpWritable*> children;
+ children.push_back(name_.get());
+ if (codeview_record_) {
+ children.push_back(codeview_record_.get());
+ }
+ if (misc_debug_record_) {
+ children.push_back(misc_debug_record_.get());
+ }
+
+ return children;
+}
+
+bool MinidumpModuleWriter::WriteObject(FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateWritable);
+
+ // This object doesn’t directly write anything itself. Its MINIDUMP_MODULE is
+ // written by its parent as part of a MINIDUMP_MODULE_LIST, and its children
+ // are responsible for writing themselves.
+ return true;
+}
+
+MinidumpModuleListWriter::MinidumpModuleListWriter()
+ : MinidumpStreamWriter(), modules_(), module_list_base_() {
+}
+
+MinidumpModuleListWriter::~MinidumpModuleListWriter() {
+}
+
+void MinidumpModuleListWriter::InitializeFromSnapshot(
+ const std::vector<const ModuleSnapshot*>& module_snapshots) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK(modules_.empty());
+
+ for (const ModuleSnapshot* module_snapshot : module_snapshots) {
+ auto module = make_scoped_ptr(new MinidumpModuleWriter());
+ module->InitializeFromSnapshot(module_snapshot);
+ AddModule(module.Pass());
+ }
+}
+
+void MinidumpModuleListWriter::AddModule(
+ scoped_ptr<MinidumpModuleWriter> module) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ modules_.push_back(module.release());
+}
+
+bool MinidumpModuleListWriter::Freeze() {
+ DCHECK_EQ(state(), kStateMutable);
+
+ if (!MinidumpStreamWriter::Freeze()) {
+ return false;
+ }
+
+ size_t module_count = modules_.size();
+ if (!AssignIfInRange(&module_list_base_.NumberOfModules, module_count)) {
+ LOG(ERROR) << "module_count " << module_count << " out of range";
+ return false;
+ }
+
+ return true;
+}
+
+size_t MinidumpModuleListWriter::SizeOfObject() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ return sizeof(module_list_base_) + modules_.size() * sizeof(MINIDUMP_MODULE);
+}
+
+std::vector<internal::MinidumpWritable*> MinidumpModuleListWriter::Children() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ std::vector<MinidumpWritable*> children;
+ for (MinidumpModuleWriter* module : modules_) {
+ children.push_back(module);
+ }
+
+ return children;
+}
+
+bool MinidumpModuleListWriter::WriteObject(FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateWritable);
+
+ WritableIoVec iov;
+ iov.iov_base = &module_list_base_;
+ iov.iov_len = sizeof(module_list_base_);
+ std::vector<WritableIoVec> iovecs(1, iov);
+
+ for (const MinidumpModuleWriter* module : modules_) {
+ iov.iov_base = module->MinidumpModule();
+ iov.iov_len = sizeof(MINIDUMP_MODULE);
+ iovecs.push_back(iov);
+ }
+
+ return file_writer->WriteIoVec(&iovecs);
+}
+
+MinidumpStreamType MinidumpModuleListWriter::StreamType() const {
+ return kMinidumpStreamTypeModuleList;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_module_writer.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_module_writer.h
new file mode 100644
index 00000000000..2bec5968506
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_module_writer.h
@@ -0,0 +1,357 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_MINIDUMP_MINIDUMP_MODULE_WRITER_H_
+#define CRASHPAD_MINIDUMP_MINIDUMP_MODULE_WRITER_H_
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <stdint.h>
+#include <time.h>
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "minidump/minidump_extensions.h"
+#include "minidump/minidump_stream_writer.h"
+#include "minidump/minidump_writable.h"
+#include "util/stdlib/pointer_container.h"
+
+namespace crashpad {
+
+class ModuleSnapshot;
+
+namespace internal {
+class MinidumpUTF16StringWriter;
+} // namespace internal
+
+//! \brief The base class for writers of CodeView records referenced by
+//! MINIDUMP_MODULE::CvRecord in minidump files.
+class MinidumpModuleCodeViewRecordWriter : public internal::MinidumpWritable {
+ public:
+ ~MinidumpModuleCodeViewRecordWriter() override;
+
+ protected:
+ MinidumpModuleCodeViewRecordWriter() : MinidumpWritable() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCodeViewRecordWriter);
+};
+
+namespace internal {
+
+//! \brief The base class for writers of CodeView records that serve as links to
+//! `.pdb` (program database) files.
+template <typename CodeViewRecordType>
+class MinidumpModuleCodeViewRecordPDBLinkWriter
+ : public MinidumpModuleCodeViewRecordWriter {
+ public:
+ //! \brief Sets the name of the `.pdb` file being linked to.
+ void SetPDBName(const std::string& pdb_name) { pdb_name_ = pdb_name; }
+
+ protected:
+ MinidumpModuleCodeViewRecordPDBLinkWriter();
+ ~MinidumpModuleCodeViewRecordPDBLinkWriter() override;
+
+ // MinidumpWritable:
+ size_t SizeOfObject() override;
+ bool WriteObject(FileWriterInterface* file_writer) override;
+
+ //! \brief Returns a pointer to the raw CodeView record’s data.
+ //!
+ //! Subclasses can use this to set fields in their codeview records other than
+ //! the `pdb_name` field.
+ CodeViewRecordType* codeview_record() { return &codeview_record_; }
+
+ private:
+ CodeViewRecordType codeview_record_;
+ std::string pdb_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCodeViewRecordPDBLinkWriter);
+};
+
+} // namespace internal
+
+//! \brief The writer for a MinidumpModuleCodeViewRecordPDB20 object in a
+//! minidump file.
+//!
+//! Most users will want MinidumpModuleCodeViewRecordPDB70Writer instead.
+class MinidumpModuleCodeViewRecordPDB20Writer final
+ : public internal::MinidumpModuleCodeViewRecordPDBLinkWriter<
+ MinidumpModuleCodeViewRecordPDB20> {
+ public:
+ MinidumpModuleCodeViewRecordPDB20Writer()
+ : internal::MinidumpModuleCodeViewRecordPDBLinkWriter<
+ MinidumpModuleCodeViewRecordPDB20>() {}
+
+ ~MinidumpModuleCodeViewRecordPDB20Writer() override;
+
+ //! \brief Sets MinidumpModuleCodeViewRecordPDB20::timestamp and
+ //! MinidumpModuleCodeViewRecordPDB20::age.
+ void SetTimestampAndAge(time_t timestamp, uint32_t age);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCodeViewRecordPDB20Writer);
+};
+
+//! \brief The writer for a MinidumpModuleCodeViewRecordPDB70 object in a
+//! minidump file.
+class MinidumpModuleCodeViewRecordPDB70Writer final
+ : public internal::MinidumpModuleCodeViewRecordPDBLinkWriter<
+ MinidumpModuleCodeViewRecordPDB70> {
+ public:
+ MinidumpModuleCodeViewRecordPDB70Writer()
+ : internal::MinidumpModuleCodeViewRecordPDBLinkWriter<
+ MinidumpModuleCodeViewRecordPDB70>() {}
+
+ ~MinidumpModuleCodeViewRecordPDB70Writer() override;
+
+ //! \brief Initializes the MinidumpModuleCodeViewRecordPDB70 based on \a
+ //! module_snapshot.
+ //!
+ //! \param[in] module_snapshot The module snapshot to use as source data.
+ //!
+ //! \note Valid in #kStateMutable. No mutator methods may be called before
+ //! this method, and it is not normally necessary to call any mutator
+ //! methods after this method.
+ void InitializeFromSnapshot(const ModuleSnapshot* module_snapshot);
+
+ //! \brief Sets MinidumpModuleCodeViewRecordPDB70::uuid and
+ //! MinidumpModuleCodeViewRecordPDB70::age.
+ void SetUUIDAndAge(const UUID& uuid, uint32_t age) {
+ codeview_record()->uuid = uuid;
+ codeview_record()->age = age;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCodeViewRecordPDB70Writer);
+};
+
+//! \brief The writer for an IMAGE_DEBUG_MISC object in a minidump file.
+//!
+//! Most users will want MinidumpModuleCodeViewRecordPDB70Writer instead.
+class MinidumpModuleMiscDebugRecordWriter final
+ : public internal::MinidumpWritable {
+ public:
+ MinidumpModuleMiscDebugRecordWriter();
+ ~MinidumpModuleMiscDebugRecordWriter() override;
+
+ //! \brief Sets IMAGE_DEBUG_MISC::DataType.
+ void SetDataType(uint32_t data_type) {
+ image_debug_misc_.DataType = data_type;
+ }
+
+ //! \brief Sets IMAGE_DEBUG_MISC::Data, IMAGE_DEBUG_MISC::Length, and
+ //! IMAGE_DEBUG_MISC::Unicode.
+ //!
+ //! If \a utf16 is `true`, \a data will be treated as UTF-8 data and will be
+ //! converted to UTF-16, and IMAGE_DEBUG_MISC::Unicode will be set to `1`.
+ //! Otherwise, \a data will be used as-is and IMAGE_DEBUG_MISC::Unicode will
+ //! be set to `0`.
+ void SetData(const std::string& data, bool utf16);
+
+ protected:
+ // MinidumpWritable:
+ bool Freeze() override;
+ size_t SizeOfObject() override;
+ bool WriteObject(FileWriterInterface* file_writer) override;
+
+ private:
+ IMAGE_DEBUG_MISC image_debug_misc_;
+ std::string data_;
+ base::string16 data_utf16_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpModuleMiscDebugRecordWriter);
+};
+
+//! \brief The writer for a MINIDUMP_MODULE object in a minidump file.
+//!
+//! Because MINIDUMP_MODULE objects only appear as elements of
+//! MINIDUMP_MODULE_LIST objects, this class does not write any data on its own.
+//! It makes its MINIDUMP_MODULE data available to its MinidumpModuleListWriter
+//! parent, which writes it as part of a MINIDUMP_MODULE_LIST.
+class MinidumpModuleWriter final : public internal::MinidumpWritable {
+ public:
+ MinidumpModuleWriter();
+ ~MinidumpModuleWriter() override;
+
+ //! \brief Initializes the MINIDUMP_MODULE based on \a module_snapshot.
+ //!
+ //! \param[in] module_snapshot The module snapshot to use as source data.
+ //!
+ //! \note Valid in #kStateMutable. No mutator methods may be called before
+ //! this method, and it is not normally necessary to call any mutator
+ //! methods after this method.
+ void InitializeFromSnapshot(const ModuleSnapshot* module_snapshot);
+
+ //! \brief Returns a MINIDUMP_MODULE referencing this object’s data.
+ //!
+ //! This method is expected to be called by a MinidumpModuleListWriter in
+ //! order to obtain a MINIDUMP_MODULE to include in its list.
+ //!
+ //! \note Valid in #kStateWritable.
+ const MINIDUMP_MODULE* MinidumpModule() const;
+
+ //! \brief Arranges for MINIDUMP_MODULE::ModuleNameRva to point to a
+ //! MINIDUMP_STRING containing \a name.
+ //!
+ //! A name is required in all MINIDUMP_MODULE objects.
+ //!
+ //! \note Valid in #kStateMutable.
+ void SetName(const std::string& name);
+
+ //! \brief Arranges for MINIDUMP_MODULE::CvRecord to point to a CodeView
+ //! record to be written by \a codeview_record.
+ //!
+ //! This object takes ownership of \a codeview_record and becomes its parent
+ //! in the overall tree of internal::MinidumpWritable objects.
+ //!
+ //! \note Valid in #kStateMutable.
+ void SetCodeViewRecord(
+ scoped_ptr<MinidumpModuleCodeViewRecordWriter> codeview_record);
+
+ //! \brief Arranges for MINIDUMP_MODULE::MiscRecord to point to an
+ //! IMAGE_DEBUG_MISC object to be written by \a misc_debug_record.
+ //!
+ //! This object takes ownership of \a misc_debug_record and becomes its parent
+ //! in the overall tree of internal::MinidumpWritable objects.
+ //!
+ //! \note Valid in #kStateMutable.
+ void SetMiscDebugRecord(
+ scoped_ptr<MinidumpModuleMiscDebugRecordWriter> misc_debug_record);
+
+ //! \brief Sets IMAGE_DEBUG_MISC::BaseOfImage.
+ void SetImageBaseAddress(uint64_t image_base_address) {
+ module_.BaseOfImage = image_base_address;
+ }
+
+ //! \brief Sets IMAGE_DEBUG_MISC::SizeOfImage.
+ void SetImageSize(uint32_t image_size) { module_.SizeOfImage = image_size; }
+
+ //! \brief Sets IMAGE_DEBUG_MISC::CheckSum.
+ void SetChecksum(uint32_t checksum) { module_.CheckSum = checksum; }
+
+ //! \brief Sets IMAGE_DEBUG_MISC::TimeDateStamp.
+ //!
+ //! \note Valid in #kStateMutable.
+ void SetTimestamp(time_t timestamp);
+
+ //! \brief Sets \ref VS_FIXEDFILEINFO::dwFileVersionMS
+ //! "IMAGE_DEBUG_MISC::VersionInfo::dwFileVersionMS" and \ref
+ //! VS_FIXEDFILEINFO::dwFileVersionLS
+ //! "IMAGE_DEBUG_MISC::VersionInfo::dwFileVersionLS".
+ //!
+ //! \note Valid in #kStateMutable.
+ void SetFileVersion(uint16_t version_0,
+ uint16_t version_1,
+ uint16_t version_2,
+ uint16_t version_3);
+
+ //! \brief Sets \ref VS_FIXEDFILEINFO::dwProductVersionMS
+ //! "IMAGE_DEBUG_MISC::VersionInfo::dwProductVersionMS" and \ref
+ //! VS_FIXEDFILEINFO::dwProductVersionLS
+ //! "IMAGE_DEBUG_MISC::VersionInfo::dwProductVersionLS".
+ //!
+ //! \note Valid in #kStateMutable.
+ void SetProductVersion(uint16_t version_0,
+ uint16_t version_1,
+ uint16_t version_2,
+ uint16_t version_3);
+
+ //! \brief Sets \ref VS_FIXEDFILEINFO::dwFileFlags
+ //! "IMAGE_DEBUG_MISC::VersionInfo::dwFileFlags" and \ref
+ //! VS_FIXEDFILEINFO::dwFileFlagsMask
+ //! "IMAGE_DEBUG_MISC::VersionInfo::dwFileFlagsMask".
+ //!
+ //! \note Valid in #kStateMutable.
+ void SetFileFlagsAndMask(uint32_t file_flags, uint32_t file_flags_mask);
+
+ //! \brief Sets \ref VS_FIXEDFILEINFO::dwFileOS
+ //! "IMAGE_DEBUG_MISC::VersionInfo::dwFileOS".
+ void SetFileOS(uint32_t file_os) { module_.VersionInfo.dwFileOS = file_os; }
+
+ //! \brief Sets \ref VS_FIXEDFILEINFO::dwFileType
+ //! "IMAGE_DEBUG_MISC::VersionInfo::dwFileType" and \ref
+ //! VS_FIXEDFILEINFO::dwFileSubtype
+ //! "IMAGE_DEBUG_MISC::VersionInfo::dwFileSubtype".
+ void SetFileTypeAndSubtype(uint32_t file_type, uint32_t file_subtype) {
+ module_.VersionInfo.dwFileType = file_type;
+ module_.VersionInfo.dwFileSubtype = file_subtype;
+ }
+
+ protected:
+ // MinidumpWritable:
+ bool Freeze() override;
+ size_t SizeOfObject() override;
+ std::vector<MinidumpWritable*> Children() override;
+ bool WriteObject(FileWriterInterface* file_writer) override;
+
+ private:
+ MINIDUMP_MODULE module_;
+ scoped_ptr<internal::MinidumpUTF16StringWriter> name_;
+ scoped_ptr<MinidumpModuleCodeViewRecordWriter> codeview_record_;
+ scoped_ptr<MinidumpModuleMiscDebugRecordWriter> misc_debug_record_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpModuleWriter);
+};
+
+//! \brief The writer for a MINIDUMP_MODULE_LIST stream in a minidump file,
+//! containing a list of MINIDUMP_MODULE objects.
+class MinidumpModuleListWriter final : public internal::MinidumpStreamWriter {
+ public:
+ MinidumpModuleListWriter();
+ ~MinidumpModuleListWriter() override;
+
+ //! \brief Adds an initialized MINIDUMP_MODULE for each module in \a
+ //! module_snapshots to the MINIDUMP_MODULE_LIST.
+ //!
+ //! \param[in] module_snapshots The module snapshots to use as source data.
+ //!
+ //! \note Valid in #kStateMutable. AddModule() may not be called before this
+ //! this method, and it is not normally necessary to call AddModule()
+ //! after this method.
+ void InitializeFromSnapshot(
+ const std::vector<const ModuleSnapshot*>& module_snapshots);
+
+ //! \brief Adds a MinidumpModuleWriter to the MINIDUMP_MODULE_LIST.
+ //!
+ //! This object takes ownership of \a module and becomes its parent in the
+ //! overall tree of internal::MinidumpWritable objects.
+ //!
+ //! \note Valid in #kStateMutable.
+ void AddModule(scoped_ptr<MinidumpModuleWriter> module);
+
+ protected:
+ // MinidumpWritable:
+ bool Freeze() override;
+ size_t SizeOfObject() override;
+ std::vector<MinidumpWritable*> Children() override;
+ bool WriteObject(FileWriterInterface* file_writer) override;
+
+ // MinidumpStreamWriter:
+ MinidumpStreamType StreamType() const override;
+
+ private:
+ PointerVector<MinidumpModuleWriter> modules_;
+ MINIDUMP_MODULE_LIST module_list_base_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpModuleListWriter);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_MINIDUMP_MINIDUMP_MODULE_WRITER_H_
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_module_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_module_writer_test.cc
new file mode 100644
index 00000000000..ab2ff20d266
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_module_writer_test.cc
@@ -0,0 +1,763 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_module_writer.h"
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "base/format_macros.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "gtest/gtest.h"
+#include "minidump/minidump_extensions.h"
+#include "minidump/minidump_file_writer.h"
+#include "minidump/test/minidump_file_writer_test_util.h"
+#include "minidump/test/minidump_string_writer_test_util.h"
+#include "minidump/test/minidump_writable_test_util.h"
+#include "snapshot/test/test_module_snapshot.h"
+#include "test/gtest_death_check.h"
+#include "util/file/string_file.h"
+#include "util/misc/uuid.h"
+#include "util/stdlib/pointer_container.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+void GetModuleListStream(const std::string& file_contents,
+ const MINIDUMP_MODULE_LIST** module_list) {
+ const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
+ const size_t kModuleListStreamOffset =
+ kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);
+ const size_t kModulesOffset =
+ kModuleListStreamOffset + sizeof(MINIDUMP_MODULE_LIST);
+
+ ASSERT_GE(file_contents.size(), kModulesOffset);
+
+ const MINIDUMP_DIRECTORY* directory;
+ const MINIDUMP_HEADER* header =
+ MinidumpHeaderAtStart(file_contents, &directory);
+ ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));
+ ASSERT_TRUE(directory);
+
+ ASSERT_EQ(kMinidumpStreamTypeModuleList, directory[0].StreamType);
+ EXPECT_EQ(kModuleListStreamOffset, directory[0].Location.Rva);
+
+ *module_list = MinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>(
+ file_contents, directory[0].Location);
+ ASSERT_TRUE(module_list);
+}
+
+TEST(MinidumpModuleWriter, EmptyModuleList) {
+ MinidumpFileWriter minidump_file_writer;
+ auto module_list_writer = make_scoped_ptr(new MinidumpModuleListWriter());
+
+ minidump_file_writer.AddStream(module_list_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
+ sizeof(MINIDUMP_MODULE_LIST),
+ string_file.string().size());
+
+ const MINIDUMP_MODULE_LIST* module_list = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ GetModuleListStream(string_file.string(), &module_list));
+
+ EXPECT_EQ(0u, module_list->NumberOfModules);
+}
+
+// If |expected_pdb_name| is not nullptr, |codeview_record| is used to locate a
+// CodeView record in |file_contents|, and its fields are compared against the
+// the |expected_pdb_*| values. If |expected_pdb_uuid| is supplied, the CodeView
+// record must be a PDB 7.0 link, otherwise, it must be a PDB 2.0 link. If
+// |expected_pdb_name| is nullptr, |codeview_record| must not point to anything.
+void ExpectCodeViewRecord(const MINIDUMP_LOCATION_DESCRIPTOR* codeview_record,
+ const std::string& file_contents,
+ const char* expected_pdb_name,
+ const UUID* expected_pdb_uuid,
+ time_t expected_pdb_timestamp,
+ uint32_t expected_pdb_age) {
+ if (expected_pdb_name) {
+ EXPECT_NE(0u, codeview_record->Rva);
+
+ std::string observed_pdb_name;
+ if (expected_pdb_uuid) {
+ // The CodeView record should be a PDB 7.0 link.
+ const MinidumpModuleCodeViewRecordPDB70* codeview_pdb70_record =
+ MinidumpWritableAtLocationDescriptor<
+ MinidumpModuleCodeViewRecordPDB70>(file_contents,
+ *codeview_record);
+ ASSERT_TRUE(codeview_pdb70_record);
+ EXPECT_EQ(0,
+ memcmp(expected_pdb_uuid,
+ &codeview_pdb70_record->uuid,
+ sizeof(codeview_pdb70_record->uuid)));
+ EXPECT_EQ(expected_pdb_age, codeview_pdb70_record->age);
+
+ observed_pdb_name.assign(
+ reinterpret_cast<const char*>(&codeview_pdb70_record->pdb_name[0]),
+ codeview_record->DataSize -
+ offsetof(MinidumpModuleCodeViewRecordPDB70, pdb_name));
+ } else {
+ // The CodeView record should be a PDB 2.0 link.
+ const MinidumpModuleCodeViewRecordPDB20* codeview_pdb20_record =
+ MinidumpWritableAtLocationDescriptor<
+ MinidumpModuleCodeViewRecordPDB20>(file_contents,
+ *codeview_record);
+ ASSERT_TRUE(codeview_pdb20_record);
+ EXPECT_EQ(static_cast<uint32_t>(expected_pdb_timestamp),
+ codeview_pdb20_record->timestamp);
+ EXPECT_EQ(expected_pdb_age, codeview_pdb20_record->age);
+
+ observed_pdb_name.assign(
+ reinterpret_cast<const char*>(&codeview_pdb20_record->pdb_name[0]),
+ codeview_record->DataSize -
+ offsetof(MinidumpModuleCodeViewRecordPDB20, pdb_name));
+ }
+
+ // Check for, and then remove, the NUL terminator.
+ EXPECT_EQ('\0', observed_pdb_name[observed_pdb_name.size() - 1]);
+ observed_pdb_name.resize(observed_pdb_name.size() - 1);
+
+ EXPECT_EQ(expected_pdb_name, observed_pdb_name);
+ } else {
+ // There should be no CodeView record.
+ EXPECT_EQ(0u, codeview_record->DataSize);
+ EXPECT_EQ(0u, codeview_record->Rva);
+ }
+}
+
+// If |expected_debug_name| is not nullptr, |misc_record| is used to locate a
+// miscellanous debugging record in |file_contents|, and its fields are compared
+// against the the |expected_debug_*| values. If |expected_debug_name| is
+// nullptr, |misc_record| must not point to anything.
+void ExpectMiscellaneousDebugRecord(
+ const MINIDUMP_LOCATION_DESCRIPTOR* misc_record,
+ const std::string& file_contents,
+ const char* expected_debug_name,
+ uint32_t expected_debug_type,
+ bool expected_debug_utf16) {
+ if (expected_debug_name) {
+ EXPECT_NE(0u, misc_record->Rva);
+ const IMAGE_DEBUG_MISC* misc_debug_record =
+ MinidumpWritableAtLocationDescriptor<IMAGE_DEBUG_MISC>(file_contents,
+ *misc_record);
+ ASSERT_TRUE(misc_debug_record);
+ EXPECT_EQ(expected_debug_type, misc_debug_record->DataType);
+ EXPECT_EQ(expected_debug_utf16,
+ static_cast<bool>(misc_debug_record->Unicode));
+ EXPECT_EQ(0u, misc_debug_record->Reserved[0]);
+ EXPECT_EQ(0u, misc_debug_record->Reserved[1]);
+ EXPECT_EQ(0u, misc_debug_record->Reserved[2]);
+
+ // Check for the NUL terminator.
+ size_t bytes_available =
+ misc_debug_record->Length - offsetof(IMAGE_DEBUG_MISC, Data);
+ EXPECT_EQ('\0', misc_debug_record->Data[bytes_available - 1]);
+ std::string observed_data(
+ reinterpret_cast<const char*>(misc_debug_record->Data));
+
+ size_t bytes_used;
+ if (misc_debug_record->Unicode) {
+ base::string16 observed_data_utf16(
+ reinterpret_cast<const base::char16*>(misc_debug_record->Data));
+ bytes_used = (observed_data_utf16.size() + 1) * sizeof(base::char16);
+ observed_data = base::UTF16ToUTF8(observed_data_utf16);
+ } else {
+ observed_data = reinterpret_cast<const char*>(misc_debug_record->Data);
+ bytes_used = (observed_data.size() + 1) * sizeof(char);
+ }
+ EXPECT_LE(bytes_used, bytes_available);
+
+ // Make sure that any padding bytes after the first NUL are also NUL.
+ for (size_t index = bytes_used; index < bytes_available; ++index) {
+ EXPECT_EQ('\0', misc_debug_record->Data[index]);
+ }
+
+ EXPECT_EQ(expected_debug_name, observed_data);
+ } else {
+ // There should be no miscellaneous debugging record.
+ EXPECT_EQ(0u, misc_record->DataSize);
+ EXPECT_EQ(0u, misc_record->Rva);
+ }
+}
+
+// ExpectModule() verifies that |expected| matches |observed|. Fields that are
+// supposed to contain constant magic numbers are verified against the expected
+// constants instead of |expected|. Reserved fields are verified to be 0. RVA
+// and MINIDUMP_LOCATION_DESCRIPTOR fields are not verified against |expected|.
+// Instead, |ModuleNameRva| is used to locate the module name, which is compared
+// against |expected_module_name|. ExpectCodeViewRecord() and
+// ExpectMiscellaneousDebugRecord() are used to verify the |CvRecord| and
+// |MiscRecord| fields against |expected_pdb_*| and |expected_debug_*|
+// parameters, respectively.
+void ExpectModule(const MINIDUMP_MODULE* expected,
+ const MINIDUMP_MODULE* observed,
+ const std::string& file_contents,
+ const std::string& expected_module_name,
+ const char* expected_pdb_name,
+ const UUID* expected_pdb_uuid,
+ time_t expected_pdb_timestamp,
+ uint32_t expected_pdb_age,
+ const char* expected_debug_name,
+ uint32_t expected_debug_type,
+ bool expected_debug_utf16) {
+ EXPECT_EQ(expected->BaseOfImage, observed->BaseOfImage);
+ EXPECT_EQ(expected->SizeOfImage, observed->SizeOfImage);
+ EXPECT_EQ(expected->CheckSum, observed->CheckSum);
+ EXPECT_EQ(expected->TimeDateStamp, observed->TimeDateStamp);
+ EXPECT_EQ(implicit_cast<uint32_t>(VS_FFI_SIGNATURE),
+ observed->VersionInfo.dwSignature);
+ EXPECT_EQ(implicit_cast<uint32_t>(VS_FFI_STRUCVERSION),
+ observed->VersionInfo.dwStrucVersion);
+ EXPECT_EQ(expected->VersionInfo.dwFileVersionMS,
+ observed->VersionInfo.dwFileVersionMS);
+ EXPECT_EQ(expected->VersionInfo.dwFileVersionLS,
+ observed->VersionInfo.dwFileVersionLS);
+ EXPECT_EQ(expected->VersionInfo.dwProductVersionMS,
+ observed->VersionInfo.dwProductVersionMS);
+ EXPECT_EQ(expected->VersionInfo.dwProductVersionLS,
+ observed->VersionInfo.dwProductVersionLS);
+ EXPECT_EQ(expected->VersionInfo.dwFileFlagsMask,
+ observed->VersionInfo.dwFileFlagsMask);
+ EXPECT_EQ(expected->VersionInfo.dwFileFlags,
+ observed->VersionInfo.dwFileFlags);
+ EXPECT_EQ(expected->VersionInfo.dwFileOS, observed->VersionInfo.dwFileOS);
+ EXPECT_EQ(expected->VersionInfo.dwFileType, observed->VersionInfo.dwFileType);
+ EXPECT_EQ(expected->VersionInfo.dwFileSubtype,
+ observed->VersionInfo.dwFileSubtype);
+ EXPECT_EQ(expected->VersionInfo.dwFileDateMS,
+ observed->VersionInfo.dwFileDateMS);
+ EXPECT_EQ(expected->VersionInfo.dwFileDateLS,
+ observed->VersionInfo.dwFileDateLS);
+ EXPECT_EQ(0u, observed->Reserved0);
+ EXPECT_EQ(0u, observed->Reserved1);
+
+ EXPECT_NE(0u, observed->ModuleNameRva);
+ base::string16 observed_module_name_utf16 =
+ MinidumpStringAtRVAAsString(file_contents, observed->ModuleNameRva);
+ base::string16 expected_module_name_utf16 =
+ base::UTF8ToUTF16(expected_module_name);
+ EXPECT_EQ(expected_module_name_utf16, observed_module_name_utf16);
+
+ ASSERT_NO_FATAL_FAILURE(ExpectCodeViewRecord(&observed->CvRecord,
+ file_contents,
+ expected_pdb_name,
+ expected_pdb_uuid,
+ expected_pdb_timestamp,
+ expected_pdb_age));
+
+ ASSERT_NO_FATAL_FAILURE(ExpectMiscellaneousDebugRecord(&observed->MiscRecord,
+ file_contents,
+ expected_debug_name,
+ expected_debug_type,
+ expected_debug_utf16));
+}
+
+TEST(MinidumpModuleWriter, EmptyModule) {
+ MinidumpFileWriter minidump_file_writer;
+ auto module_list_writer = make_scoped_ptr(new MinidumpModuleListWriter());
+
+ const char kModuleName[] = "test_executable";
+
+ auto module_writer = make_scoped_ptr(new MinidumpModuleWriter());
+ module_writer->SetName(kModuleName);
+
+ module_list_writer->AddModule(module_writer.Pass());
+ minidump_file_writer.AddStream(module_list_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ ASSERT_GT(string_file.string().size(),
+ sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
+ sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE));
+
+ const MINIDUMP_MODULE_LIST* module_list = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ GetModuleListStream(string_file.string(), &module_list));
+
+ EXPECT_EQ(1u, module_list->NumberOfModules);
+
+ MINIDUMP_MODULE expected = {};
+ ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected,
+ &module_list->Modules[0],
+ string_file.string(),
+ kModuleName,
+ nullptr,
+ nullptr,
+ 0,
+ 0,
+ nullptr,
+ 0,
+ false));
+}
+
+TEST(MinidumpModuleWriter, OneModule) {
+ MinidumpFileWriter minidump_file_writer;
+ auto module_list_writer = make_scoped_ptr(new MinidumpModuleListWriter());
+
+ const char kModuleName[] = "statically_linked";
+ const uint64_t kModuleBase = 0x10da69000;
+ const uint32_t kModuleSize = 0x1000;
+ const uint32_t kChecksum = 0x76543210;
+ const time_t kTimestamp = 0x386d4380;
+ const uint32_t kFileVersionMS = 0x00010002;
+ const uint32_t kFileVersionLS = 0x00030004;
+ const uint32_t kProductVersionMS = 0x00050006;
+ const uint32_t kProductVersionLS = 0x00070008;
+ const uint32_t kFileFlagsMask = VS_FF_DEBUG | VS_FF_PRERELEASE |
+ VS_FF_PATCHED | VS_FF_PRIVATEBUILD |
+ VS_FF_INFOINFERRED | VS_FF_SPECIALBUILD;
+ const uint32_t kFileFlags = VS_FF_PRIVATEBUILD | VS_FF_SPECIALBUILD;
+ const uint32_t kFileOS = VOS_DOS;
+ const uint32_t kFileType = VFT_DRV;
+ const uint32_t kFileSubtype = VFT2_DRV_KEYBOARD;
+ const char kPDBName[] = "statical.pdb";
+ const uint8_t kPDBUUIDBytes[16] =
+ {0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10,
+ 0x08, 0x19, 0x2a, 0x3b, 0x4c, 0x5d, 0x6e, 0x7f};
+ UUID pdb_uuid;
+ pdb_uuid.InitializeFromBytes(kPDBUUIDBytes);
+ const uint32_t kPDBAge = 1;
+ const uint32_t kDebugType = IMAGE_DEBUG_MISC_EXENAME;
+ const char kDebugName[] = "statical.dbg";
+ const bool kDebugUTF16 = false;
+
+ auto module_writer = make_scoped_ptr(new MinidumpModuleWriter());
+ module_writer->SetName(kModuleName);
+ module_writer->SetImageBaseAddress(kModuleBase);
+ module_writer->SetImageSize(kModuleSize);
+ module_writer->SetChecksum(kChecksum);
+ module_writer->SetTimestamp(kTimestamp);
+ module_writer->SetFileVersion(kFileVersionMS >> 16,
+ kFileVersionMS & 0xffff,
+ kFileVersionLS >> 16,
+ kFileVersionLS & 0xffff);
+ module_writer->SetProductVersion(kProductVersionMS >> 16,
+ kProductVersionMS & 0xffff,
+ kProductVersionLS >> 16,
+ kProductVersionLS & 0xffff);
+ module_writer->SetFileFlagsAndMask(kFileFlags, kFileFlagsMask);
+ module_writer->SetFileOS(kFileOS);
+ module_writer->SetFileTypeAndSubtype(kFileType, kFileSubtype);
+
+ auto codeview_pdb70_writer =
+ make_scoped_ptr(new MinidumpModuleCodeViewRecordPDB70Writer());
+ codeview_pdb70_writer->SetPDBName(kPDBName);
+ codeview_pdb70_writer->SetUUIDAndAge(pdb_uuid, kPDBAge);
+ module_writer->SetCodeViewRecord(codeview_pdb70_writer.Pass());
+
+ auto misc_debug_writer =
+ make_scoped_ptr(new MinidumpModuleMiscDebugRecordWriter());
+ misc_debug_writer->SetDataType(kDebugType);
+ misc_debug_writer->SetData(kDebugName, kDebugUTF16);
+ module_writer->SetMiscDebugRecord(misc_debug_writer.Pass());
+
+ module_list_writer->AddModule(module_writer.Pass());
+ minidump_file_writer.AddStream(module_list_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ ASSERT_GT(string_file.string().size(),
+ sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
+ sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE));
+
+ const MINIDUMP_MODULE_LIST* module_list = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ GetModuleListStream(string_file.string(), &module_list));
+
+ EXPECT_EQ(1u, module_list->NumberOfModules);
+
+ MINIDUMP_MODULE expected = {};
+ expected.BaseOfImage = kModuleBase;
+ expected.SizeOfImage = kModuleSize;
+ expected.CheckSum = kChecksum;
+ expected.TimeDateStamp = kTimestamp;
+ expected.VersionInfo.dwFileVersionMS = kFileVersionMS;
+ expected.VersionInfo.dwFileVersionLS = kFileVersionLS;
+ expected.VersionInfo.dwProductVersionMS = kProductVersionMS;
+ expected.VersionInfo.dwProductVersionLS = kProductVersionLS;
+ expected.VersionInfo.dwFileFlagsMask = kFileFlagsMask;
+ expected.VersionInfo.dwFileFlags = kFileFlags;
+ expected.VersionInfo.dwFileOS = kFileOS;
+ expected.VersionInfo.dwFileType = kFileType;
+ expected.VersionInfo.dwFileSubtype = kFileSubtype;
+
+ ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected,
+ &module_list->Modules[0],
+ string_file.string(),
+ kModuleName,
+ kPDBName,
+ &pdb_uuid,
+ 0,
+ kPDBAge,
+ kDebugName,
+ kDebugType,
+ kDebugUTF16));
+}
+
+TEST(MinidumpModuleWriter, OneModule_CodeViewUsesPDB20_MiscUsesUTF16) {
+ // MinidumpModuleWriter.OneModule tested with a PDB 7.0 link as the CodeView
+ // record and an IMAGE_DEBUG_MISC record in UTF-8. This test exercises the
+ // alternatives, a PDB 2.0 link as the CodeView record and an IMAGE_DEBUG_MISC
+ // record with UTF-16 data.
+ MinidumpFileWriter minidump_file_writer;
+ auto module_list_writer = make_scoped_ptr(new MinidumpModuleListWriter());
+
+ const char kModuleName[] = "dinosaur";
+ const char kPDBName[] = "d1n05.pdb";
+ const time_t kPDBTimestamp = 0x386d4380;
+ const uint32_t kPDBAge = 1;
+ const uint32_t kDebugType = IMAGE_DEBUG_MISC_EXENAME;
+ const char kDebugName[] = "d1n05.dbg";
+ const bool kDebugUTF16 = true;
+
+ auto module_writer = make_scoped_ptr(new MinidumpModuleWriter());
+ module_writer->SetName(kModuleName);
+
+ auto codeview_pdb20_writer =
+ make_scoped_ptr(new MinidumpModuleCodeViewRecordPDB20Writer());
+ codeview_pdb20_writer->SetPDBName(kPDBName);
+ codeview_pdb20_writer->SetTimestampAndAge(kPDBTimestamp, kPDBAge);
+ module_writer->SetCodeViewRecord(codeview_pdb20_writer.Pass());
+
+ auto misc_debug_writer =
+ make_scoped_ptr(new MinidumpModuleMiscDebugRecordWriter());
+ misc_debug_writer->SetDataType(kDebugType);
+ misc_debug_writer->SetData(kDebugName, kDebugUTF16);
+ module_writer->SetMiscDebugRecord(misc_debug_writer.Pass());
+
+ module_list_writer->AddModule(module_writer.Pass());
+ minidump_file_writer.AddStream(module_list_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ ASSERT_GT(string_file.string().size(),
+ sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
+ sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE));
+
+ const MINIDUMP_MODULE_LIST* module_list = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ GetModuleListStream(string_file.string(), &module_list));
+
+ EXPECT_EQ(1u, module_list->NumberOfModules);
+
+ MINIDUMP_MODULE expected = {};
+
+ ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected,
+ &module_list->Modules[0],
+ string_file.string(),
+ kModuleName,
+ kPDBName,
+ nullptr,
+ kPDBTimestamp,
+ kPDBAge,
+ kDebugName,
+ kDebugType,
+ kDebugUTF16));
+}
+
+TEST(MinidumpModuleWriter, ThreeModules) {
+ // As good exercise, this test uses three modules, one with a PDB 7.0 link as
+ // its CodeView record, one with no CodeView record, and one with a PDB 2.0
+ // link as its CodeView record.
+ MinidumpFileWriter minidump_file_writer;
+ auto module_list_writer = make_scoped_ptr(new MinidumpModuleListWriter());
+
+ const char kModuleName0[] = "main";
+ const uint64_t kModuleBase0 = 0x100101000;
+ const uint32_t kModuleSize0 = 0xf000;
+ const char kPDBName0[] = "main";
+ const uint8_t kPDBUUIDBytes0[16] =
+ {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11,
+ 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99};
+ UUID pdb_uuid_0;
+ pdb_uuid_0.InitializeFromBytes(kPDBUUIDBytes0);
+ const uint32_t kPDBAge0 = 0;
+
+ const char kModuleName1[] = "ld.so";
+ const uint64_t kModuleBase1 = 0x200202000;
+ const uint32_t kModuleSize1 = 0x1e000;
+
+ const char kModuleName2[] = "libc.so";
+ const uint64_t kModuleBase2 = 0x300303000;
+ const uint32_t kModuleSize2 = 0x2d000;
+ const char kPDBName2[] = "libc.so";
+ const time_t kPDBTimestamp2 = 0x386d4380;
+ const uint32_t kPDBAge2 = 2;
+
+ auto module_writer_0 = make_scoped_ptr(new MinidumpModuleWriter());
+ module_writer_0->SetName(kModuleName0);
+ module_writer_0->SetImageBaseAddress(kModuleBase0);
+ module_writer_0->SetImageSize(kModuleSize0);
+
+ auto codeview_pdb70_writer_0 =
+ make_scoped_ptr(new MinidumpModuleCodeViewRecordPDB70Writer());
+ codeview_pdb70_writer_0->SetPDBName(kPDBName0);
+ codeview_pdb70_writer_0->SetUUIDAndAge(pdb_uuid_0, kPDBAge0);
+ module_writer_0->SetCodeViewRecord(codeview_pdb70_writer_0.Pass());
+
+ module_list_writer->AddModule(module_writer_0.Pass());
+
+ auto module_writer_1 = make_scoped_ptr(new MinidumpModuleWriter());
+ module_writer_1->SetName(kModuleName1);
+ module_writer_1->SetImageBaseAddress(kModuleBase1);
+ module_writer_1->SetImageSize(kModuleSize1);
+
+ module_list_writer->AddModule(module_writer_1.Pass());
+
+ auto module_writer_2 = make_scoped_ptr(new MinidumpModuleWriter());
+ module_writer_2->SetName(kModuleName2);
+ module_writer_2->SetImageBaseAddress(kModuleBase2);
+ module_writer_2->SetImageSize(kModuleSize2);
+
+ auto codeview_pdb70_writer_2 =
+ make_scoped_ptr(new MinidumpModuleCodeViewRecordPDB20Writer());
+ codeview_pdb70_writer_2->SetPDBName(kPDBName2);
+ codeview_pdb70_writer_2->SetTimestampAndAge(kPDBTimestamp2, kPDBAge2);
+ module_writer_2->SetCodeViewRecord(codeview_pdb70_writer_2.Pass());
+
+ module_list_writer->AddModule(module_writer_2.Pass());
+
+ minidump_file_writer.AddStream(module_list_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ ASSERT_GT(string_file.string().size(),
+ sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
+ sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE));
+
+ const MINIDUMP_MODULE_LIST* module_list = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ GetModuleListStream(string_file.string(), &module_list));
+
+ EXPECT_EQ(3u, module_list->NumberOfModules);
+
+ MINIDUMP_MODULE expected = {};
+
+ {
+ SCOPED_TRACE("module 0");
+
+ expected.BaseOfImage = kModuleBase0;
+ expected.SizeOfImage = kModuleSize0;
+
+ ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected,
+ &module_list->Modules[0],
+ string_file.string(),
+ kModuleName0,
+ kPDBName0,
+ &pdb_uuid_0,
+ 0,
+ kPDBAge0,
+ nullptr,
+ 0,
+ false));
+ }
+
+ {
+ SCOPED_TRACE("module 1");
+
+ expected.BaseOfImage = kModuleBase1;
+ expected.SizeOfImage = kModuleSize1;
+
+ ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected,
+ &module_list->Modules[1],
+ string_file.string(),
+ kModuleName1,
+ nullptr,
+ nullptr,
+ 0,
+ 0,
+ nullptr,
+ 0,
+ false));
+ }
+
+ {
+ SCOPED_TRACE("module 2");
+
+ expected.BaseOfImage = kModuleBase2;
+ expected.SizeOfImage = kModuleSize2;
+
+ ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected,
+ &module_list->Modules[2],
+ string_file.string(),
+ kModuleName2,
+ kPDBName2,
+ nullptr,
+ kPDBTimestamp2,
+ kPDBAge2,
+ nullptr,
+ 0,
+ false));
+ }
+}
+
+void InitializeTestModuleSnapshotFromMinidumpModule(
+ TestModuleSnapshot* module_snapshot,
+ const MINIDUMP_MODULE& minidump_module,
+ const std::string& name,
+ const crashpad::UUID& uuid) {
+ module_snapshot->SetName(name);
+
+ module_snapshot->SetAddressAndSize(minidump_module.BaseOfImage,
+ minidump_module.SizeOfImage);
+ module_snapshot->SetTimestamp(minidump_module.TimeDateStamp);
+ module_snapshot->SetFileVersion(
+ minidump_module.VersionInfo.dwFileVersionMS >> 16,
+ minidump_module.VersionInfo.dwFileVersionMS & 0xffff,
+ minidump_module.VersionInfo.dwFileVersionLS >> 16,
+ minidump_module.VersionInfo.dwFileVersionLS & 0xffff);
+ module_snapshot->SetSourceVersion(
+ minidump_module.VersionInfo.dwProductVersionMS >> 16,
+ minidump_module.VersionInfo.dwProductVersionMS & 0xffff,
+ minidump_module.VersionInfo.dwProductVersionLS >> 16,
+ minidump_module.VersionInfo.dwProductVersionLS & 0xffff);
+
+ ModuleSnapshot::ModuleType module_type;
+ switch (minidump_module.VersionInfo.dwFileType) {
+ case VFT_APP:
+ module_type = ModuleSnapshot::kModuleTypeExecutable;
+ break;
+ case VFT_DLL:
+ module_type = ModuleSnapshot::kModuleTypeSharedLibrary;
+ break;
+ default:
+ module_type = ModuleSnapshot::kModuleTypeUnknown;
+ break;
+ }
+ module_snapshot->SetModuleType(module_type);
+
+ module_snapshot->SetUUID(uuid);
+}
+
+TEST(MinidumpModuleWriter, InitializeFromSnapshot) {
+ MINIDUMP_MODULE expect_modules[3] = {};
+ const char* module_paths[arraysize(expect_modules)] = {};
+ const char* module_names[arraysize(expect_modules)] = {};
+ UUID uuids[arraysize(expect_modules)] = {};
+
+ expect_modules[0].BaseOfImage = 0x100101000;
+ expect_modules[0].SizeOfImage = 0xf000;
+ expect_modules[0].TimeDateStamp = 0x01234567;
+ expect_modules[0].VersionInfo.dwFileVersionMS = 0x00010002;
+ expect_modules[0].VersionInfo.dwFileVersionLS = 0x00030004;
+ expect_modules[0].VersionInfo.dwProductVersionMS = 0x00050006;
+ expect_modules[0].VersionInfo.dwProductVersionLS = 0x00070008;
+ expect_modules[0].VersionInfo.dwFileType = VFT_APP;
+ module_paths[0] = "/usr/bin/true";
+ module_names[0] = "true";
+ const uint8_t kUUIDBytes0[16] =
+ {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
+ uuids[0].InitializeFromBytes(kUUIDBytes0);
+
+ expect_modules[1].BaseOfImage = 0x200202000;
+ expect_modules[1].SizeOfImage = 0x1e1000;
+ expect_modules[1].TimeDateStamp = 0x89abcdef;
+ expect_modules[1].VersionInfo.dwFileVersionMS = 0x0009000a;
+ expect_modules[1].VersionInfo.dwFileVersionLS = 0x000b000c;
+ expect_modules[1].VersionInfo.dwProductVersionMS = 0x000d000e;
+ expect_modules[1].VersionInfo.dwProductVersionLS = 0x000f0000;
+ expect_modules[1].VersionInfo.dwFileType = VFT_DLL;
+ module_paths[1] = "/usr/lib/libSystem.B.dylib";
+ module_names[1] = "libSystem.B.dylib";
+ const uint8_t kUUIDBytes1[16] =
+ {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
+ uuids[1].InitializeFromBytes(kUUIDBytes1);
+
+ expect_modules[2].BaseOfImage = 0x300303000;
+ expect_modules[2].SizeOfImage = 0x2d000;
+ expect_modules[2].TimeDateStamp = 0x76543210;
+ expect_modules[2].VersionInfo.dwFileVersionMS = 0x11112222;
+ expect_modules[2].VersionInfo.dwFileVersionLS = 0x33334444;
+ expect_modules[2].VersionInfo.dwProductVersionMS = 0x9999aaaa;
+ expect_modules[2].VersionInfo.dwProductVersionLS = 0xbbbbcccc;
+ expect_modules[2].VersionInfo.dwFileType = VFT_UNKNOWN;
+ module_paths[2] = "/usr/lib/dyld";
+ module_names[2] = "dyld";
+ const uint8_t kUUIDBytes2[16] =
+ {0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
+ 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0};
+ uuids[2].InitializeFromBytes(kUUIDBytes2);
+
+ PointerVector<TestModuleSnapshot> module_snapshots_owner;
+ std::vector<const ModuleSnapshot*> module_snapshots;
+ for (size_t index = 0; index < arraysize(expect_modules); ++index) {
+ TestModuleSnapshot* module_snapshot = new TestModuleSnapshot();
+ module_snapshots_owner.push_back(module_snapshot);
+ InitializeTestModuleSnapshotFromMinidumpModule(module_snapshot,
+ expect_modules[index],
+ module_paths[index],
+ uuids[index]);
+ module_snapshots.push_back(module_snapshot);
+ }
+
+ auto module_list_writer = make_scoped_ptr(new MinidumpModuleListWriter());
+ module_list_writer->InitializeFromSnapshot(module_snapshots);
+
+ MinidumpFileWriter minidump_file_writer;
+ minidump_file_writer.AddStream(module_list_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_MODULE_LIST* module_list = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ GetModuleListStream(string_file.string(), &module_list));
+
+ ASSERT_EQ(3u, module_list->NumberOfModules);
+
+ for (size_t index = 0; index < module_list->NumberOfModules; ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index));
+ ASSERT_NO_FATAL_FAILURE(ExpectModule(&expect_modules[index],
+ &module_list->Modules[index],
+ string_file.string(),
+ module_paths[index],
+ module_names[index],
+ &uuids[index],
+ 0,
+ 0,
+ nullptr,
+ 0,
+ false));
+ }
+}
+
+TEST(MinidumpModuleWriterDeathTest, NoModuleName) {
+ MinidumpFileWriter minidump_file_writer;
+ auto module_list_writer = make_scoped_ptr(new MinidumpModuleListWriter());
+ auto module_writer = make_scoped_ptr(new MinidumpModuleWriter());
+ module_list_writer->AddModule(module_writer.Pass());
+ minidump_file_writer.AddStream(module_list_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_DEATH_CHECK(minidump_file_writer.WriteEverything(&string_file),
+ "name_");
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer.cc
new file mode 100644
index 00000000000..74508305083
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer.cc
@@ -0,0 +1,98 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_rva_list_writer.h"
+
+#include "base/logging.h"
+#include "util/file/file_writer.h"
+#include "util/numeric/safe_assignment.h"
+
+namespace crashpad {
+namespace internal {
+
+MinidumpRVAListWriter::MinidumpRVAListWriter()
+ : MinidumpWritable(),
+ rva_list_base_(new MinidumpRVAList()),
+ children_(),
+ child_rvas_() {
+}
+
+MinidumpRVAListWriter::~MinidumpRVAListWriter() {
+}
+
+void MinidumpRVAListWriter::AddChild(scoped_ptr<MinidumpWritable> child) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ children_.push_back(child.release());
+}
+
+bool MinidumpRVAListWriter::Freeze() {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK(child_rvas_.empty());
+
+ if (!MinidumpWritable::Freeze()) {
+ return false;
+ }
+
+ size_t child_count = children_.size();
+ if (!AssignIfInRange(&rva_list_base_->count, child_count)) {
+ LOG(ERROR) << "child_count " << child_count << " out of range";
+ return false;
+ }
+
+ child_rvas_.resize(child_count);
+ for (size_t index = 0; index < child_count; ++index) {
+ children_[index]->RegisterRVA(&child_rvas_[index]);
+ }
+
+ return true;
+}
+
+size_t MinidumpRVAListWriter::SizeOfObject() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ return sizeof(*rva_list_base_) + children_.size() * sizeof(RVA);
+}
+
+std::vector<MinidumpWritable*> MinidumpRVAListWriter::Children() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ std::vector<MinidumpWritable*> children;
+ for (MinidumpWritable* child : children_) {
+ children.push_back(child);
+ }
+
+ return children;
+}
+
+bool MinidumpRVAListWriter::WriteObject(FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateWritable);
+ DCHECK_EQ(children_.size(), child_rvas_.size());
+
+ WritableIoVec iov;
+ iov.iov_base = rva_list_base_.get();
+ iov.iov_len = sizeof(*rva_list_base_);
+ std::vector<WritableIoVec> iovecs(1, iov);
+
+ if (!child_rvas_.empty()) {
+ iov.iov_base = &child_rvas_[0];
+ iov.iov_len = child_rvas_.size() * sizeof(RVA);
+ iovecs.push_back(iov);
+ }
+
+ return file_writer->WriteIoVec(&iovecs);
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer.h
new file mode 100644
index 00000000000..42bb1b515ee
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer.h
@@ -0,0 +1,79 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_MINIDUMP_RVA_LIST_WRITER_H_
+#define CRASHPAD_MINIDUMP_RVA_LIST_WRITER_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "minidump/minidump_extensions.h"
+#include "minidump/minidump_writable.h"
+#include "util/stdlib/pointer_container.h"
+
+namespace crashpad {
+namespace internal {
+
+//! \brief The writer for a MinidumpRVAList object in a minidump file,
+//! containing a list of ::RVA pointers.
+class MinidumpRVAListWriter : public MinidumpWritable {
+ protected:
+ MinidumpRVAListWriter();
+ ~MinidumpRVAListWriter() override;
+
+ //! \brief Adds an ::RVA referencing an MinidumpWritable to the
+ //! MinidumpRVAList.
+ //!
+ //! This object takes ownership of \a child and becomes its parent in the
+ //! overall tree of MinidumpWritable objects.
+ //!
+ //! To provide type-correctness, subclasses are expected to provide a public
+ //! method that accepts a `scoped_ptr`-wrapped argument of the proper
+ //! MinidumpWritable subclass, and call this method with that argument.
+ //!
+ //! \note Valid in #kStateMutable.
+ void AddChild(scoped_ptr<MinidumpWritable> child);
+
+ //! \brief Returns `true` if no child objects have been added by AddChild(),
+ //! and `false` if child objects are present.
+ bool IsEmpty() const { return children_.empty(); }
+
+ //! \brief Returns an object’s ::RVA objects referencing its children.
+ //!
+ //! \note The returned vector will be empty until the object advances to
+ //! #kStateFrozen or beyond.
+ const std::vector<RVA>& child_rvas() const { return child_rvas_; }
+
+ // MinidumpWritable:
+ bool Freeze() override;
+ size_t SizeOfObject() override;
+ std::vector<MinidumpWritable*> Children() override;
+ bool WriteObject(FileWriterInterface* file_writer) override;
+
+ private:
+ scoped_ptr<MinidumpRVAList> rva_list_base_;
+ PointerVector<MinidumpWritable> children_;
+ std::vector<RVA> child_rvas_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpRVAListWriter);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_MINIDUMP_RVA_LIST_WRITER_H_
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer_test.cc
new file mode 100644
index 00000000000..f2594003fb2
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer_test.cc
@@ -0,0 +1,102 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_rva_list_writer.h"
+
+#include "base/format_macros.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+#include "minidump/test/minidump_rva_list_test_util.h"
+#include "minidump/test/minidump_writable_test_util.h"
+#include "util/file/string_file.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+class TestMinidumpRVAListWriter final : public internal::MinidumpRVAListWriter {
+ public:
+ TestMinidumpRVAListWriter() : MinidumpRVAListWriter() {}
+ ~TestMinidumpRVAListWriter() override {}
+
+ void AddChild(uint32_t value) {
+ auto child = make_scoped_ptr(new TestUInt32MinidumpWritable(value));
+ MinidumpRVAListWriter::AddChild(child.Pass());
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestMinidumpRVAListWriter);
+};
+
+TEST(MinidumpRVAListWriter, Empty) {
+ TestMinidumpRVAListWriter list_writer;
+
+ StringFile string_file;
+
+ ASSERT_TRUE(list_writer.WriteEverything(&string_file));
+ EXPECT_EQ(sizeof(MinidumpRVAList), string_file.string().size());
+
+ const MinidumpRVAList* list = MinidumpRVAListAtStart(string_file.string(), 0);
+ ASSERT_TRUE(list);
+}
+
+TEST(MinidumpRVAListWriter, OneChild) {
+ TestMinidumpRVAListWriter list_writer;
+
+ const uint32_t kValue = 0;
+ list_writer.AddChild(kValue);
+
+ StringFile string_file;
+
+ ASSERT_TRUE(list_writer.WriteEverything(&string_file));
+
+ const MinidumpRVAList* list = MinidumpRVAListAtStart(string_file.string(), 1);
+ ASSERT_TRUE(list);
+
+ const uint32_t* child = MinidumpWritableAtRVA<uint32_t>(
+ string_file.string(), list->children[0]);
+ ASSERT_TRUE(child);
+ EXPECT_EQ(kValue, *child);
+}
+
+TEST(MinidumpRVAListWriter, ThreeChildren) {
+ TestMinidumpRVAListWriter list_writer;
+
+ const uint32_t kValues[] = { 0x80000000, 0x55555555, 0x66006600 };
+
+ list_writer.AddChild(kValues[0]);
+ list_writer.AddChild(kValues[1]);
+ list_writer.AddChild(kValues[2]);
+
+ StringFile string_file;
+
+ ASSERT_TRUE(list_writer.WriteEverything(&string_file));
+
+ const MinidumpRVAList* list =
+ MinidumpRVAListAtStart(string_file.string(), arraysize(kValues));
+ ASSERT_TRUE(list);
+
+ for (size_t index = 0; index < arraysize(kValues); ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index));
+
+ const uint32_t* child = MinidumpWritableAtRVA<uint32_t>(
+ string_file.string(), list->children[index]);
+ ASSERT_TRUE(child);
+ EXPECT_EQ(kValues[index], *child);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_simple_string_dictionary_writer.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_simple_string_dictionary_writer.cc
new file mode 100644
index 00000000000..c7d81e2fde4
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_simple_string_dictionary_writer.cc
@@ -0,0 +1,187 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_simple_string_dictionary_writer.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "util/file/file_writer.h"
+#include "util/numeric/safe_assignment.h"
+
+namespace crashpad {
+
+MinidumpSimpleStringDictionaryEntryWriter::
+ MinidumpSimpleStringDictionaryEntryWriter()
+ : MinidumpWritable(), entry_(), key_(), value_() {
+}
+
+MinidumpSimpleStringDictionaryEntryWriter::
+ ~MinidumpSimpleStringDictionaryEntryWriter() {
+}
+
+const MinidumpSimpleStringDictionaryEntry*
+MinidumpSimpleStringDictionaryEntryWriter::MinidumpSimpleStringDictionaryEntry()
+ const {
+ DCHECK_EQ(state(), kStateWritable);
+
+ return &entry_;
+}
+
+void MinidumpSimpleStringDictionaryEntryWriter::SetKeyValue(
+ const std::string& key,
+ const std::string& value) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ key_.SetUTF8(key);
+ value_.SetUTF8(value);
+}
+
+bool MinidumpSimpleStringDictionaryEntryWriter::Freeze() {
+ DCHECK_EQ(state(), kStateMutable);
+
+ if (!MinidumpWritable::Freeze()) {
+ return false;
+ }
+
+ key_.RegisterRVA(&entry_.key);
+ value_.RegisterRVA(&entry_.value);
+
+ return true;
+}
+
+size_t MinidumpSimpleStringDictionaryEntryWriter::SizeOfObject() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ // This object doesn’t directly write anything itself. Its
+ // MinidumpSimpleStringDictionaryEntry is written by its parent as part of a
+ // MinidumpSimpleStringDictionary, and its children are responsible for
+ // writing themselves.
+ return 0;
+}
+
+std::vector<internal::MinidumpWritable*>
+MinidumpSimpleStringDictionaryEntryWriter::Children() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ std::vector<MinidumpWritable*> children(1, &key_);
+ children.push_back(&value_);
+ return children;
+}
+
+bool MinidumpSimpleStringDictionaryEntryWriter::WriteObject(
+ FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateWritable);
+
+ // This object doesn’t directly write anything itself. Its
+ // MinidumpSimpleStringDictionaryEntry is written by its parent as part of a
+ // MinidumpSimpleStringDictionary, and its children are responsible for
+ // writing themselves.
+ return true;
+}
+
+MinidumpSimpleStringDictionaryWriter::MinidumpSimpleStringDictionaryWriter()
+ : MinidumpWritable(),
+ entries_(),
+ simple_string_dictionary_base_(new MinidumpSimpleStringDictionary()) {
+}
+
+MinidumpSimpleStringDictionaryWriter::~MinidumpSimpleStringDictionaryWriter() {
+ STLDeleteContainerPairSecondPointers(entries_.begin(), entries_.end());
+}
+
+void MinidumpSimpleStringDictionaryWriter::InitializeFromMap(
+ const std::map<std::string, std::string>& map) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK(entries_.empty());
+
+ for (const auto& iterator : map) {
+ auto entry =
+ make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter());
+ entry->SetKeyValue(iterator.first, iterator.second);
+ AddEntry(entry.Pass());
+ }
+}
+
+void MinidumpSimpleStringDictionaryWriter::AddEntry(
+ scoped_ptr<MinidumpSimpleStringDictionaryEntryWriter> entry) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ const std::string& key = entry->Key();
+ auto iterator = entries_.find(key);
+ if (iterator != entries_.end()) {
+ delete iterator->second;
+ iterator->second = entry.release();
+ } else {
+ entries_[key] = entry.release();
+ }
+}
+
+bool MinidumpSimpleStringDictionaryWriter::IsUseful() const {
+ return !entries_.empty();
+}
+
+bool MinidumpSimpleStringDictionaryWriter::Freeze() {
+ DCHECK_EQ(state(), kStateMutable);
+
+ if (!MinidumpWritable::Freeze()) {
+ return false;
+ }
+
+ size_t entry_count = entries_.size();
+ if (!AssignIfInRange(&simple_string_dictionary_base_->count, entry_count)) {
+ LOG(ERROR) << "entry_count " << entry_count << " out of range";
+ return false;
+ }
+
+ return true;
+}
+
+size_t MinidumpSimpleStringDictionaryWriter::SizeOfObject() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ return sizeof(*simple_string_dictionary_base_) +
+ entries_.size() * sizeof(MinidumpSimpleStringDictionaryEntry);
+}
+
+std::vector<internal::MinidumpWritable*>
+MinidumpSimpleStringDictionaryWriter::Children() {
+ DCHECK_GE(state(), kStateMutable);
+
+ std::vector<MinidumpWritable*> children;
+ for (const auto& key_entry : entries_) {
+ children.push_back(key_entry.second);
+ }
+
+ return children;
+}
+
+bool MinidumpSimpleStringDictionaryWriter::WriteObject(
+ FileWriterInterface* file_writer) {
+ DCHECK_GE(state(), kStateWritable);
+
+ WritableIoVec iov;
+ iov.iov_base = simple_string_dictionary_base_.get();
+ iov.iov_len = sizeof(*simple_string_dictionary_base_);
+ std::vector<WritableIoVec> iovecs(1, iov);
+
+ for (const auto& key_entry : entries_) {
+ iov.iov_base = key_entry.second->MinidumpSimpleStringDictionaryEntry();
+ iov.iov_len = sizeof(MinidumpSimpleStringDictionaryEntry);
+ iovecs.push_back(iov);
+ }
+
+ return file_writer->WriteIoVec(&iovecs);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_simple_string_dictionary_writer.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_simple_string_dictionary_writer.h
new file mode 100644
index 00000000000..88059bd8eb5
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_simple_string_dictionary_writer.h
@@ -0,0 +1,145 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_WRITER_H_
+#define CRASHPAD_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_WRITER_H_
+
+#include <sys/types.h>
+
+#include <string>
+#include <map>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "minidump/minidump_extensions.h"
+#include "minidump/minidump_string_writer.h"
+#include "minidump/minidump_writable.h"
+
+namespace crashpad {
+
+//! \brief The writer for a MinidumpSimpleStringDictionaryEntry object in a
+//! minidump file.
+//!
+//! Because MinidumpSimpleStringDictionaryEntry objects only appear as elements
+//! of MinidumpSimpleStringDictionary objects, this class does not write any
+//! data on its own. It makes its MinidumpSimpleStringDictionaryEntry data
+//! available to its MinidumpSimpleStringDictionaryWriter parent, which writes
+//! it as part of a MinidumpSimpleStringDictionary.
+class MinidumpSimpleStringDictionaryEntryWriter final
+ : public internal::MinidumpWritable {
+ public:
+ MinidumpSimpleStringDictionaryEntryWriter();
+ ~MinidumpSimpleStringDictionaryEntryWriter() override;
+
+ //! \brief Returns a MinidumpSimpleStringDictionaryEntry referencing this
+ //! object’s data.
+ //!
+ //! This method is expected to be called by a
+ //! MinidumpSimpleStringDictionaryWriter in order to obtain a
+ //! MinidumpSimpleStringDictionaryEntry to include in its list.
+ //!
+ //! \note Valid in #kStateWritable.
+ const MinidumpSimpleStringDictionaryEntry*
+ MinidumpSimpleStringDictionaryEntry() const;
+
+ //! \brief Sets the strings to be written as the entry object’s key and value.
+ //!
+ //! \note Valid in #kStateMutable.
+ void SetKeyValue(const std::string& key, const std::string& value);
+
+ //! \brief Retrieves the key to be written.
+ //!
+ //! \note Valid in any state.
+ const std::string& Key() const { return key_.UTF8(); }
+
+ protected:
+ // MinidumpWritable:
+
+ bool Freeze() override;
+ size_t SizeOfObject() override;
+ std::vector<MinidumpWritable*> Children() override;
+ bool WriteObject(FileWriterInterface* file_writer) override;
+
+ private:
+ struct MinidumpSimpleStringDictionaryEntry entry_;
+ internal::MinidumpUTF8StringWriter key_;
+ internal::MinidumpUTF8StringWriter value_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpSimpleStringDictionaryEntryWriter);
+};
+
+//! \brief The writer for a MinidumpSimpleStringDictionary object in a minidump
+//! file, containing a list of MinidumpSimpleStringDictionaryEntry objects.
+//!
+//! Because this class writes a representatin of a dictionary, the order of
+//! entries is insignificant. Entries may be written in any order.
+class MinidumpSimpleStringDictionaryWriter final
+ : public internal::MinidumpWritable {
+ public:
+ MinidumpSimpleStringDictionaryWriter();
+ ~MinidumpSimpleStringDictionaryWriter() override;
+
+ //! \brief Adds an initialized MinidumpSimpleStringDictionaryEntryWriter for
+ //! each key-value pair in \a map to the MinidumpSimpleStringDictionary.
+ //!
+ //! \param[in] map The map to use as source data.
+ //!
+ //! \note Valid in #kStateMutable. No mutator methods may be called before
+ //! this method, and it is not normally necessary to call any mutator
+ //! methods after this method.
+ void InitializeFromMap(const std::map<std::string, std::string>& map);
+
+ //! \brief Adds a MinidumpSimpleStringDictionaryEntryWriter to the
+ //! MinidumpSimpleStringDictionary.
+ //!
+ //! This object takes ownership of \a entry and becomes its parent in the
+ //! overall tree of internal::MinidumpWritable objects.
+ //!
+ //! If the key contained in \a entry duplicates the key of an entry already
+ //! present in the MinidumpSimpleStringDictionary, the new \a entry will
+ //! replace the previous one.
+ //!
+ //! \note Valid in #kStateMutable.
+ void AddEntry(scoped_ptr<MinidumpSimpleStringDictionaryEntryWriter> entry);
+
+ //! \brief Determines whether the object is useful.
+ //!
+ //! A useful object is one that carries data that makes a meaningful
+ //! contribution to a minidump file. An object carrying entries would be
+ //! considered useful.
+ //!
+ //! \return `true` if the object is useful, `false` otherwise.
+ bool IsUseful() const;
+
+ protected:
+ // MinidumpWritable:
+
+ bool Freeze() override;
+ size_t SizeOfObject() override;
+ std::vector<MinidumpWritable*> Children() override;
+ bool WriteObject(FileWriterInterface* file_writer) override;
+
+ private:
+ // This object owns the MinidumpSimpleStringDictionaryEntryWriter objects.
+ std::map<std::string, MinidumpSimpleStringDictionaryEntryWriter*> entries_;
+
+ scoped_ptr<MinidumpSimpleStringDictionary> simple_string_dictionary_base_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpSimpleStringDictionaryWriter);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_WRITER_H_
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_simple_string_dictionary_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_simple_string_dictionary_writer_test.cc
new file mode 100644
index 00000000000..0f9a71bd288
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_simple_string_dictionary_writer_test.cc
@@ -0,0 +1,287 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_simple_string_dictionary_writer.h"
+
+#include <map>
+#include <string>
+
+#include "gtest/gtest.h"
+#include "minidump/minidump_extensions.h"
+#include "minidump/test/minidump_string_writer_test_util.h"
+#include "minidump/test/minidump_writable_test_util.h"
+#include "util/file/string_file.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+const MinidumpSimpleStringDictionary* MinidumpSimpleStringDictionaryAtStart(
+ const std::string& file_contents,
+ size_t count) {
+ MINIDUMP_LOCATION_DESCRIPTOR location_descriptor;
+ location_descriptor.DataSize = static_cast<uint32_t>(
+ sizeof(MinidumpSimpleStringDictionary) +
+ count * sizeof(MinidumpSimpleStringDictionaryEntry));
+ location_descriptor.Rva = 0;
+ return MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(
+ file_contents, location_descriptor);
+}
+
+TEST(MinidumpSimpleStringDictionaryWriter, EmptySimpleStringDictionary) {
+ StringFile string_file;
+
+ MinidumpSimpleStringDictionaryWriter dictionary_writer;
+
+ EXPECT_FALSE(dictionary_writer.IsUseful());
+
+ EXPECT_TRUE(dictionary_writer.WriteEverything(&string_file));
+ ASSERT_EQ(sizeof(MinidumpSimpleStringDictionary),
+ string_file.string().size());
+
+ const MinidumpSimpleStringDictionary* dictionary =
+ MinidumpSimpleStringDictionaryAtStart(string_file.string(), 0);
+ ASSERT_TRUE(dictionary);
+ EXPECT_EQ(0u, dictionary->count);
+}
+
+TEST(MinidumpSimpleStringDictionaryWriter, EmptyKeyValue) {
+ StringFile string_file;
+
+ MinidumpSimpleStringDictionaryWriter dictionary_writer;
+ auto entry_writer =
+ make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter());
+ dictionary_writer.AddEntry(entry_writer.Pass());
+
+ EXPECT_TRUE(dictionary_writer.IsUseful());
+
+ EXPECT_TRUE(dictionary_writer.WriteEverything(&string_file));
+ ASSERT_EQ(sizeof(MinidumpSimpleStringDictionary) +
+ sizeof(MinidumpSimpleStringDictionaryEntry) +
+ 2 * sizeof(MinidumpUTF8String) + 1 + 3 + 1, // 3 for padding
+ string_file.string().size());
+
+ const MinidumpSimpleStringDictionary* dictionary =
+ MinidumpSimpleStringDictionaryAtStart(string_file.string(), 1);
+ ASSERT_TRUE(dictionary);
+ EXPECT_EQ(1u, dictionary->count);
+ EXPECT_EQ(12u, dictionary->entries[0].key);
+ EXPECT_EQ(20u, dictionary->entries[0].value);
+ EXPECT_EQ("",
+ MinidumpUTF8StringAtRVAAsString(string_file.string(),
+ dictionary->entries[0].key));
+ EXPECT_EQ("",
+ MinidumpUTF8StringAtRVAAsString(string_file.string(),
+ dictionary->entries[0].value));
+}
+
+TEST(MinidumpSimpleStringDictionaryWriter, OneKeyValue) {
+ StringFile string_file;
+
+ char kKey[] = "key";
+ char kValue[] = "value";
+
+ MinidumpSimpleStringDictionaryWriter dictionary_writer;
+ auto entry_writer =
+ make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter());
+ entry_writer->SetKeyValue(kKey, kValue);
+ dictionary_writer.AddEntry(entry_writer.Pass());
+
+ EXPECT_TRUE(dictionary_writer.IsUseful());
+
+ EXPECT_TRUE(dictionary_writer.WriteEverything(&string_file));
+ ASSERT_EQ(sizeof(MinidumpSimpleStringDictionary) +
+ sizeof(MinidumpSimpleStringDictionaryEntry) +
+ 2 * sizeof(MinidumpUTF8String) + sizeof(kKey) + sizeof(kValue),
+ string_file.string().size());
+
+ const MinidumpSimpleStringDictionary* dictionary =
+ MinidumpSimpleStringDictionaryAtStart(string_file.string(), 1);
+ ASSERT_TRUE(dictionary);
+ EXPECT_EQ(1u, dictionary->count);
+ EXPECT_EQ(12u, dictionary->entries[0].key);
+ EXPECT_EQ(20u, dictionary->entries[0].value);
+ EXPECT_EQ(kKey,
+ MinidumpUTF8StringAtRVAAsString(string_file.string(),
+ dictionary->entries[0].key));
+ EXPECT_EQ(kValue,
+ MinidumpUTF8StringAtRVAAsString(string_file.string(),
+ dictionary->entries[0].value));
+}
+
+TEST(MinidumpSimpleStringDictionaryWriter, ThreeKeysValues) {
+ StringFile string_file;
+
+ char kKey0[] = "m0";
+ char kValue0[] = "value0";
+ char kKey1[] = "zzz1";
+ char kValue1[] = "v1";
+ char kKey2[] = "aa2";
+ char kValue2[] = "val2";
+
+ MinidumpSimpleStringDictionaryWriter dictionary_writer;
+ auto entry_writer_0 =
+ make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter());
+ entry_writer_0->SetKeyValue(kKey0, kValue0);
+ dictionary_writer.AddEntry(entry_writer_0.Pass());
+ auto entry_writer_1 =
+ make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter());
+ entry_writer_1->SetKeyValue(kKey1, kValue1);
+ dictionary_writer.AddEntry(entry_writer_1.Pass());
+ auto entry_writer_2 =
+ make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter());
+ entry_writer_2->SetKeyValue(kKey2, kValue2);
+ dictionary_writer.AddEntry(entry_writer_2.Pass());
+
+ EXPECT_TRUE(dictionary_writer.IsUseful());
+
+ EXPECT_TRUE(dictionary_writer.WriteEverything(&string_file));
+ ASSERT_EQ(sizeof(MinidumpSimpleStringDictionary) +
+ 3 * sizeof(MinidumpSimpleStringDictionaryEntry) +
+ 6 * sizeof(MinidumpUTF8String) + sizeof(kKey2) +
+ sizeof(kValue2) + 3 + sizeof(kKey0) + 1 + sizeof(kValue0) + 1 +
+ sizeof(kKey1) + 3 + sizeof(kValue1),
+ string_file.string().size());
+
+ const MinidumpSimpleStringDictionary* dictionary =
+ MinidumpSimpleStringDictionaryAtStart(string_file.string(), 3);
+ ASSERT_TRUE(dictionary);
+ EXPECT_EQ(3u, dictionary->count);
+ EXPECT_EQ(28u, dictionary->entries[0].key);
+ EXPECT_EQ(36u, dictionary->entries[0].value);
+ EXPECT_EQ(48u, dictionary->entries[1].key);
+ EXPECT_EQ(56u, dictionary->entries[1].value);
+ EXPECT_EQ(68u, dictionary->entries[2].key);
+ EXPECT_EQ(80u, dictionary->entries[2].value);
+
+ // The entries don’t appear in the order they were added. The current
+ // implementation uses a std::map and sorts keys, so the entires appear in
+ // alphabetical order. However, this is an implementation detail, and it’s OK
+ // if the writer stops sorting in this order. Testing for a specific order is
+ // just the easiest way to write this test while the writer will output things
+ // in a known order.
+ EXPECT_EQ(kKey2,
+ MinidumpUTF8StringAtRVAAsString(string_file.string(),
+ dictionary->entries[0].key));
+ EXPECT_EQ(kValue2,
+ MinidumpUTF8StringAtRVAAsString(string_file.string(),
+ dictionary->entries[0].value));
+ EXPECT_EQ(kKey0,
+ MinidumpUTF8StringAtRVAAsString(string_file.string(),
+ dictionary->entries[1].key));
+ EXPECT_EQ(kValue0,
+ MinidumpUTF8StringAtRVAAsString(string_file.string(),
+ dictionary->entries[1].value));
+ EXPECT_EQ(kKey1,
+ MinidumpUTF8StringAtRVAAsString(string_file.string(),
+ dictionary->entries[2].key));
+ EXPECT_EQ(kValue1,
+ MinidumpUTF8StringAtRVAAsString(string_file.string(),
+ dictionary->entries[2].value));
+}
+
+TEST(MinidumpSimpleStringDictionaryWriter, DuplicateKeyValue) {
+ StringFile string_file;
+
+ char kKey[] = "key";
+ char kValue0[] = "fake_value";
+ char kValue1[] = "value";
+
+ MinidumpSimpleStringDictionaryWriter dictionary_writer;
+ auto entry_writer_0 =
+ make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter());
+ entry_writer_0->SetKeyValue(kKey, kValue0);
+ dictionary_writer.AddEntry(entry_writer_0.Pass());
+ auto entry_writer_1 =
+ make_scoped_ptr(new MinidumpSimpleStringDictionaryEntryWriter());
+ entry_writer_1->SetKeyValue(kKey, kValue1);
+ dictionary_writer.AddEntry(entry_writer_1.Pass());
+
+ EXPECT_TRUE(dictionary_writer.IsUseful());
+
+ EXPECT_TRUE(dictionary_writer.WriteEverything(&string_file));
+ ASSERT_EQ(sizeof(MinidumpSimpleStringDictionary) +
+ sizeof(MinidumpSimpleStringDictionaryEntry) +
+ 2 * sizeof(MinidumpUTF8String) + sizeof(kKey) + sizeof(kValue1),
+ string_file.string().size());
+
+ const MinidumpSimpleStringDictionary* dictionary =
+ MinidumpSimpleStringDictionaryAtStart(string_file.string(), 1);
+ ASSERT_TRUE(dictionary);
+ EXPECT_EQ(1u, dictionary->count);
+ EXPECT_EQ(12u, dictionary->entries[0].key);
+ EXPECT_EQ(20u, dictionary->entries[0].value);
+ EXPECT_EQ(kKey,
+ MinidumpUTF8StringAtRVAAsString(string_file.string(),
+ dictionary->entries[0].key));
+ EXPECT_EQ(kValue1,
+ MinidumpUTF8StringAtRVAAsString(string_file.string(),
+ dictionary->entries[0].value));
+}
+
+TEST(MinidumpSimpleStringDictionaryWriter, InitializeFromMap) {
+ char kKey0[] = "Dictionaries";
+ char kValue0[] = "USEFUL*";
+ char kKey1[] = "#1 Key!";
+ char kValue1[] = "";
+ char kKey2[] = "key two";
+ char kValue2[] = "value two";
+
+ std::map<std::string, std::string> map;
+ map[kKey0] = kValue0;
+ map[kKey1] = kValue1;
+ map[kKey2] = kValue2;
+
+ MinidumpSimpleStringDictionaryWriter dictionary_writer;
+ dictionary_writer.InitializeFromMap(map);
+
+ EXPECT_TRUE(dictionary_writer.IsUseful());
+
+ StringFile string_file;
+ ASSERT_TRUE(dictionary_writer.WriteEverything(&string_file));
+
+ const MinidumpSimpleStringDictionary* dictionary =
+ MinidumpSimpleStringDictionaryAtStart(string_file.string(), map.size());
+ ASSERT_TRUE(dictionary);
+ ASSERT_EQ(3u, dictionary->count);
+
+ // The entries don’t appear in the order they were added. The current
+ // implementation uses a std::map and sorts keys, so the entires appear in
+ // alphabetical order. However, this is an implementation detail, and it’s OK
+ // if the writer stops sorting in this order. Testing for a specific order is
+ // just the easiest way to write this test while the writer will output things
+ // in a known order.
+ EXPECT_EQ(kKey1,
+ MinidumpUTF8StringAtRVAAsString(string_file.string(),
+ dictionary->entries[0].key));
+ EXPECT_EQ(kValue1,
+ MinidumpUTF8StringAtRVAAsString(string_file.string(),
+ dictionary->entries[0].value));
+ EXPECT_EQ(kKey0,
+ MinidumpUTF8StringAtRVAAsString(string_file.string(),
+ dictionary->entries[1].key));
+ EXPECT_EQ(kValue0,
+ MinidumpUTF8StringAtRVAAsString(string_file.string(),
+ dictionary->entries[1].value));
+ EXPECT_EQ(kKey2,
+ MinidumpUTF8StringAtRVAAsString(string_file.string(),
+ dictionary->entries[2].key));
+ EXPECT_EQ(kValue2,
+ MinidumpUTF8StringAtRVAAsString(string_file.string(),
+ dictionary->entries[2].value));
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_stream_writer.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_stream_writer.cc
new file mode 100644
index 00000000000..8e5dcb30b85
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_stream_writer.cc
@@ -0,0 +1,49 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_stream_writer.h"
+
+#include "base/logging.h"
+
+namespace crashpad {
+namespace internal {
+
+MinidumpStreamWriter::~MinidumpStreamWriter() {
+}
+
+const MINIDUMP_DIRECTORY* MinidumpStreamWriter::DirectoryListEntry() const {
+ DCHECK_EQ(state(), kStateWritable);
+
+ return &directory_list_entry_;
+}
+
+MinidumpStreamWriter::MinidumpStreamWriter()
+ : MinidumpWritable(), directory_list_entry_() {
+}
+
+bool MinidumpStreamWriter::Freeze() {
+ DCHECK_EQ(state(), kStateMutable);
+
+ if (!MinidumpWritable::Freeze()) {
+ return false;
+ }
+
+ directory_list_entry_.StreamType = StreamType();
+ RegisterLocationDescriptor(&directory_list_entry_.Location);
+
+ return true;
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_stream_writer.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_stream_writer.h
new file mode 100644
index 00000000000..c812f5f9302
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_stream_writer.h
@@ -0,0 +1,66 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_MINIDUMP_MINIDUMP_STREAM_WRITER_H_
+#define CRASHPAD_MINIDUMP_MINIDUMP_STREAM_WRITER_H_
+
+#include <windows.h>
+#include <dbghelp.h>
+
+#include "base/basictypes.h"
+#include "minidump/minidump_extensions.h"
+#include "minidump/minidump_writable.h"
+
+namespace crashpad {
+namespace internal {
+
+//! \brief The base class for all second-level objects (“streams”) in a minidump
+//! file.
+//!
+//! Instances of subclasses of this class are children of the root-level
+//! MinidumpFileWriter object.
+class MinidumpStreamWriter : public MinidumpWritable {
+ public:
+ ~MinidumpStreamWriter() override;
+
+ //! \brief Returns an object’s stream type.
+ //!
+ //! \note Valid in any state.
+ virtual MinidumpStreamType StreamType() const = 0;
+
+ //! \brief Returns a MINIDUMP_DIRECTORY entry that serves as a pointer to this
+ //! stream.
+ //!
+ //! This method is provided for MinidumpFileWriter, which calls it in order to
+ //! obtain the directory entry for a stream.
+ //!
+ //! \note Valid only in #kStateWritable.
+ const MINIDUMP_DIRECTORY* DirectoryListEntry() const;
+
+ protected:
+ MinidumpStreamWriter();
+
+ // MinidumpWritable:
+ bool Freeze() override;
+
+ private:
+ MINIDUMP_DIRECTORY directory_list_entry_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpStreamWriter);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_MINIDUMP_STREAM_WRITER_H_
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_string_writer.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_string_writer.cc
new file mode 100644
index 00000000000..95c0ad59b70
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_string_writer.cc
@@ -0,0 +1,138 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_string_writer.h"
+
+#include <sys/types.h>
+
+#include "base/logging.h"
+#include "minidump/minidump_writer_util.h"
+#include "util/file/file_writer.h"
+#include "util/numeric/safe_assignment.h"
+
+namespace crashpad {
+namespace internal {
+
+template <typename Traits>
+MinidumpStringWriter<Traits>::MinidumpStringWriter()
+ : MinidumpWritable(), string_base_(new MinidumpStringType()), string_() {
+}
+
+template <typename Traits>
+MinidumpStringWriter<Traits>::~MinidumpStringWriter() {
+}
+
+template <typename Traits>
+bool MinidumpStringWriter<Traits>::Freeze() {
+ DCHECK_EQ(state(), kStateMutable);
+
+ if (!MinidumpWritable::Freeze()) {
+ return false;
+ }
+
+ size_t string_bytes = string_.size() * sizeof(string_[0]);
+ if (!AssignIfInRange(&string_base_->Length, string_bytes)) {
+ LOG(ERROR) << "string_bytes " << string_bytes << " out of range";
+ return false;
+ }
+
+ return true;
+}
+
+template <typename Traits>
+size_t MinidumpStringWriter<Traits>::SizeOfObject() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ // Include the NUL terminator.
+ return sizeof(*string_base_) + (string_.size() + 1) * sizeof(string_[0]);
+}
+
+template <typename Traits>
+bool MinidumpStringWriter<Traits>::WriteObject(
+ FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateWritable);
+
+ // The string’s length is stored in string_base_, and its data is stored in
+ // string_. Write them both.
+ WritableIoVec iov;
+ iov.iov_base = string_base_.get();
+ iov.iov_len = sizeof(*string_base_);
+ std::vector<WritableIoVec> iovecs(1, iov);
+
+ // Include the NUL terminator.
+ iov.iov_base = &string_[0];
+ iov.iov_len = (string_.size() + 1) * sizeof(string_[0]);
+ iovecs.push_back(iov);
+
+ return file_writer->WriteIoVec(&iovecs);
+}
+
+// Explicit template instantiation of the forms of MinidumpStringWriter<> used
+// as base classes.
+template class MinidumpStringWriter<MinidumpStringWriterUTF16Traits>;
+template class MinidumpStringWriter<MinidumpStringWriterUTF8Traits>;
+
+MinidumpUTF16StringWriter::~MinidumpUTF16StringWriter() {
+}
+
+void MinidumpUTF16StringWriter::SetUTF8(const std::string& string_utf8) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ set_string(MinidumpWriterUtil::ConvertUTF8ToUTF16(string_utf8));
+}
+
+MinidumpUTF8StringWriter::~MinidumpUTF8StringWriter() {
+}
+
+template <typename MinidumpStringWriterType>
+MinidumpStringListWriter<MinidumpStringWriterType>::MinidumpStringListWriter()
+ : MinidumpRVAListWriter() {
+}
+
+template <typename MinidumpStringWriterType>
+MinidumpStringListWriter<
+ MinidumpStringWriterType>::~MinidumpStringListWriter() {
+}
+
+template <typename MinidumpStringWriterType>
+void MinidumpStringListWriter<MinidumpStringWriterType>::InitializeFromVector(
+ const std::vector<std::string>& vector) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK(IsEmpty());
+
+ for (const std::string& string : vector) {
+ AddStringUTF8(string);
+ }
+}
+
+template <typename MinidumpStringWriterType>
+void MinidumpStringListWriter<MinidumpStringWriterType>::AddStringUTF8(
+ const std::string& string_utf8) {
+ auto string_writer = make_scoped_ptr(new MinidumpStringWriterType());
+ string_writer->SetUTF8(string_utf8);
+ AddChild(string_writer.Pass());
+}
+
+template <typename MinidumpStringWriterType>
+bool MinidumpStringListWriter<MinidumpStringWriterType>::IsUseful() const {
+ return !IsEmpty();
+}
+
+// Explicit template instantiation of the forms of MinidumpStringListWriter<>
+// used as type aliases.
+template class MinidumpStringListWriter<MinidumpUTF16StringWriter>;
+template class MinidumpStringListWriter<MinidumpUTF8StringWriter>;
+
+} // namespace internal
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_string_writer.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_string_writer.h
new file mode 100644
index 00000000000..ec57179ee9a
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_string_writer.h
@@ -0,0 +1,183 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_MINIDUMP_MINIDUMP_STRING_WRITER_H_
+#define CRASHPAD_MINIDUMP_MINIDUMP_STRING_WRITER_H_
+
+#include <windows.h>
+#include <dbghelp.h>
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "minidump/minidump_extensions.h"
+#include "minidump/minidump_rva_list_writer.h"
+#include "minidump/minidump_writable.h"
+
+namespace crashpad {
+namespace internal {
+
+//! \cond
+
+struct MinidumpStringWriterUTF16Traits {
+ using StringType = base::string16;
+ using MinidumpStringType = MINIDUMP_STRING;
+};
+
+struct MinidumpStringWriterUTF8Traits {
+ using StringType = std::string;
+ using MinidumpStringType = MinidumpUTF8String;
+};
+
+//! \endcond
+
+//! \brief Writes a variable-length string to a minidump file in accordance with
+//! the string type’s characteristics.
+//!
+//! MinidumpStringWriter objects should not be instantiated directly. To write
+//! strings to minidump file, use the MinidumpUTF16StringWriter and
+//! MinidumpUTF8StringWriter subclasses instead.
+template <typename Traits>
+class MinidumpStringWriter : public MinidumpWritable {
+ public:
+ MinidumpStringWriter();
+ ~MinidumpStringWriter() override;
+
+ protected:
+ using MinidumpStringType = typename Traits::MinidumpStringType;
+ using StringType = typename Traits::StringType;
+
+ bool Freeze() override;
+ size_t SizeOfObject() override;
+ bool WriteObject(FileWriterInterface* file_writer) override;
+
+ //! \brief Sets the string to be written.
+ //!
+ //! \note Valid in #kStateMutable.
+ void set_string(const StringType& string) { string_.assign(string); }
+
+ //! \brief Retrieves the string to be written.
+ //!
+ //! \note Valid in any state.
+ const StringType& string() const { return string_; }
+
+ private:
+ scoped_ptr<MinidumpStringType> string_base_;
+ StringType string_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpStringWriter);
+};
+
+//! \brief Writes a variable-length UTF-16-encoded MINIDUMP_STRING to a minidump
+//! file.
+//!
+//! MinidumpUTF16StringWriter objects should not be instantiated directly
+//! outside of the MinidumpWritable family of classes.
+class MinidumpUTF16StringWriter final
+ : public MinidumpStringWriter<MinidumpStringWriterUTF16Traits> {
+ public:
+ MinidumpUTF16StringWriter() : MinidumpStringWriter() {}
+ ~MinidumpUTF16StringWriter() override;
+
+ //! \brief Converts a UTF-8 string to UTF-16 and sets it as the string to be
+ //! written.
+ //!
+ //! \note Valid in #kStateMutable.
+ void SetUTF8(const std::string& string_utf8);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MinidumpUTF16StringWriter);
+};
+
+//! \brief Writes a variable-length UTF-8-encoded MinidumpUTF8String to a
+//! minidump file.
+//!
+//! MinidumpUTF8StringWriter objects should not be instantiated directly outside
+//! of the MinidumpWritable family of classes.
+class MinidumpUTF8StringWriter final
+ : public MinidumpStringWriter<MinidumpStringWriterUTF8Traits> {
+ public:
+ MinidumpUTF8StringWriter() : MinidumpStringWriter() {}
+ ~MinidumpUTF8StringWriter() override;
+
+ //! \brief Sets the string to be written.
+ //!
+ //! \note Valid in #kStateMutable.
+ void SetUTF8(const std::string& string_utf8) { set_string(string_utf8); }
+
+ //! \brief Retrieves the string to be written.
+ //!
+ //! \note Valid in any state.
+ const std::string& UTF8() const { return string(); }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MinidumpUTF8StringWriter);
+};
+
+//! \brief The writer for a MinidumpRVAList object in a minidump file,
+//! containing a list of \a MinidumpStringWriterType objects.
+template <typename MinidumpStringWriterType>
+class MinidumpStringListWriter final : public MinidumpRVAListWriter {
+ public:
+ MinidumpStringListWriter();
+ ~MinidumpStringListWriter() override;
+
+ //! \brief Adds a new \a Traits::MinidumpStringWriterType for each element in
+ //! \a vector to the MinidumpRVAList.
+ //!
+ //! \param[in] vector The vector to use as source data. Each string in the
+ //! vector is treated as a UTF-8 string, and a new string writer will be
+ //! created for each one and made a child of the MinidumpStringListWriter.
+ //!
+ //! \note Valid in #kStateMutable. No mutator methods may be called before
+ //! this method, and it is not normally necessary to call any mutator
+ //! methods after this method.
+ void InitializeFromVector(const std::vector<std::string>& vector);
+
+ //! \brief Creates a new \a Traits::MinidumpStringWriterType object and adds
+ //! it to the MinidumpRVAList.
+ //!
+ //! This object creates a new string writer with string value \a string_utf8,
+ //! takes ownership of it, and becomes its parent in the overall tree of
+ //! MinidumpWritable objects.
+ //!
+ //! \note Valid in #kStateMutable.
+ void AddStringUTF8(const std::string& string_utf8);
+
+ //! \brief Determines whether the object is useful.
+ //!
+ //! A useful object is one that carries data that makes a meaningful
+ //! contribution to a minidump file. An object carrying entries would be
+ //! considered useful.
+ //!
+ //! \return `true` if the object is useful, `false` otherwise.
+ bool IsUseful() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MinidumpStringListWriter);
+};
+
+} // namespace internal
+
+using MinidumpUTF16StringListWriter = internal::MinidumpStringListWriter<
+ internal::MinidumpUTF16StringWriter>;
+using MinidumpUTF8StringListWriter = internal::MinidumpStringListWriter<
+ internal::MinidumpUTF8StringWriter>;
+
+} // namespace crashpad
+
+#endif // CRASHPAD_MINIDUMP_MINIDUMP_STRING_WRITER_H_
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc
new file mode 100644
index 00000000000..aa8e48c6a3c
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc
@@ -0,0 +1,268 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_string_writer.h"
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/format_macros.h"
+#include "base/strings/string16.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "gtest/gtest.h"
+#include "minidump/test/minidump_rva_list_test_util.h"
+#include "minidump/test/minidump_string_writer_test_util.h"
+#include "minidump/test/minidump_writable_test_util.h"
+#include "util/file/string_file.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(MinidumpStringWriter, MinidumpUTF16StringWriter) {
+ StringFile string_file;
+
+ {
+ SCOPED_TRACE("unset");
+ string_file.Reset();
+ crashpad::internal::MinidumpUTF16StringWriter string_writer;
+ EXPECT_TRUE(string_writer.WriteEverything(&string_file));
+ ASSERT_EQ(6u, string_file.string().size());
+
+ const MINIDUMP_STRING* minidump_string =
+ MinidumpStringAtRVA(string_file.string(), 0);
+ EXPECT_TRUE(minidump_string);
+ EXPECT_EQ(base::string16(),
+ MinidumpStringAtRVAAsString(string_file.string(), 0));
+ }
+
+ const struct {
+ size_t input_length;
+ const char* input_string;
+ size_t output_length;
+ base::char16 output_string[10];
+ } kTestData[] = {
+ {0, "", 0, {}},
+ {1, "a", 1, {'a'}},
+ {2, "\0b", 2, {0, 'b'}},
+ {3, "cde", 3, {'c', 'd', 'e'}},
+ {9, "Hi world!", 9, {'H', 'i', ' ', 'w', 'o', 'r', 'l', 'd', '!'}},
+ {7, "ret\nurn", 7, {'r', 'e', 't', '\n', 'u', 'r', 'n'}},
+ {2, "\303\251", 1, {0x00e9}}, // é
+
+ // oóöőo
+ {8, "o\303\263\303\266\305\221o", 5, {'o', 0x00f3, 0x00f6, 0x151, 'o'}},
+ {4, "\360\220\204\202", 2, {0xd800, 0xdd02}}, // 𐄂 (non-BMP)
+ };
+
+ for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ SCOPED_TRACE(base::StringPrintf(
+ "index %" PRIuS ", input %s", index, kTestData[index].input_string));
+
+ // Make sure that the expected output string with its NUL terminator fits in
+ // the space provided.
+ ASSERT_EQ(
+ 0,
+ kTestData[index]
+ .output_string[arraysize(kTestData[index].output_string) - 1]);
+
+ string_file.Reset();
+ crashpad::internal::MinidumpUTF16StringWriter string_writer;
+ string_writer.SetUTF8(std::string(kTestData[index].input_string,
+ kTestData[index].input_length));
+ EXPECT_TRUE(string_writer.WriteEverything(&string_file));
+
+ const size_t expected_utf16_units_with_nul =
+ kTestData[index].output_length + 1;
+ MINIDUMP_STRING tmp = {0};
+ ALLOW_UNUSED_LOCAL(tmp);
+ const size_t expected_utf16_bytes =
+ expected_utf16_units_with_nul * sizeof(tmp.Buffer[0]);
+ ASSERT_EQ(sizeof(MINIDUMP_STRING) + expected_utf16_bytes,
+ string_file.string().size());
+
+ const MINIDUMP_STRING* minidump_string =
+ MinidumpStringAtRVA(string_file.string(), 0);
+ EXPECT_TRUE(minidump_string);
+ base::string16 expect_string = base::string16(
+ kTestData[index].output_string, kTestData[index].output_length);
+ EXPECT_EQ(expect_string,
+ MinidumpStringAtRVAAsString(string_file.string(), 0));
+ }
+}
+
+TEST(MinidumpStringWriter, ConvertInvalidUTF8ToUTF16) {
+ StringFile string_file;
+
+ const char* kTestData[] = {
+ "\200", // continuation byte
+ "\300", // start byte followed by EOF
+ "\310\177", // start byte without continuation
+ "\340\200", // EOF in middle of 3-byte sequence
+ "\340\200\115", // invalid 3-byte sequence
+ "\303\0\251", // NUL in middle of valid sequence
+ };
+
+ for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ SCOPED_TRACE(base::StringPrintf(
+ "index %" PRIuS ", input %s", index, kTestData[index]));
+ string_file.Reset();
+ crashpad::internal::MinidumpUTF16StringWriter string_writer;
+ string_writer.SetUTF8(kTestData[index]);
+ EXPECT_TRUE(string_writer.WriteEverything(&string_file));
+
+ // The requirements for conversion of invalid UTF-8 input are lax. Make sure
+ // that at least enough data was written for a string that has one unit and
+ // a NUL terminator, make sure that the length field matches the length of
+ // data written, and make sure that at least one U+FFFD replacement
+ // character was written.
+ const MINIDUMP_STRING* minidump_string =
+ MinidumpStringAtRVA(string_file.string(), 0);
+ EXPECT_TRUE(minidump_string);
+ MINIDUMP_STRING tmp = {0};
+ ALLOW_UNUSED_LOCAL(tmp);
+ EXPECT_EQ(string_file.string().size() - sizeof(MINIDUMP_STRING) -
+ sizeof(tmp.Buffer[0]),
+ minidump_string->Length);
+ base::string16 output_string =
+ MinidumpStringAtRVAAsString(string_file.string(), 0);
+ EXPECT_FALSE(output_string.empty());
+ EXPECT_NE(base::string16::npos, output_string.find(0xfffd));
+ }
+}
+
+TEST(MinidumpStringWriter, MinidumpUTF8StringWriter) {
+ StringFile string_file;
+
+ {
+ SCOPED_TRACE("unset");
+ string_file.Reset();
+ crashpad::internal::MinidumpUTF8StringWriter string_writer;
+ EXPECT_TRUE(string_writer.WriteEverything(&string_file));
+ ASSERT_EQ(5u, string_file.string().size());
+
+ const MinidumpUTF8String* minidump_string =
+ MinidumpUTF8StringAtRVA(string_file.string(), 0);
+ EXPECT_TRUE(minidump_string);
+ EXPECT_EQ(std::string(),
+ MinidumpUTF8StringAtRVAAsString(string_file.string(), 0));
+ }
+
+ const struct {
+ size_t length;
+ const char* string;
+ } kTestData[] = {
+ {0, ""},
+ {1, "a"},
+ {2, "\0b"},
+ {3, "cde"},
+ {9, "Hi world!"},
+ {7, "ret\nurn"},
+ {2, "\303\251"}, // é
+
+ // oóöőo
+ {8, "o\303\263\303\266\305\221o"},
+ {4, "\360\220\204\202"}, // 𐄂 (non-BMP)
+ };
+
+ for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ SCOPED_TRACE(base::StringPrintf(
+ "index %" PRIuS ", input %s", index, kTestData[index].string));
+
+ string_file.Reset();
+ crashpad::internal::MinidumpUTF8StringWriter string_writer;
+ std::string test_string(kTestData[index].string, kTestData[index].length);
+ string_writer.SetUTF8(test_string);
+ EXPECT_EQ(test_string, string_writer.UTF8());
+ EXPECT_TRUE(string_writer.WriteEverything(&string_file));
+
+ const size_t expected_utf8_bytes_with_nul = kTestData[index].length + 1;
+ ASSERT_EQ(sizeof(MinidumpUTF8String) + expected_utf8_bytes_with_nul,
+ string_file.string().size());
+
+ const MinidumpUTF8String* minidump_string =
+ MinidumpUTF8StringAtRVA(string_file.string(), 0);
+ EXPECT_TRUE(minidump_string);
+ EXPECT_EQ(test_string,
+ MinidumpUTF8StringAtRVAAsString(string_file.string(), 0));
+ }
+}
+
+struct MinidumpUTF16StringListWriterTraits {
+ using MinidumpStringListWriterType = MinidumpUTF16StringListWriter;
+ static base::string16 ExpectationForUTF8(const std::string& utf8) {
+ return base::UTF8ToUTF16(utf8);
+ }
+ static base::string16 ObservationAtRVA(const std::string& file_contents,
+ RVA rva) {
+ return MinidumpStringAtRVAAsString(file_contents, rva);
+ }
+};
+
+struct MinidumpUTF8StringListWriterTraits {
+ using MinidumpStringListWriterType = MinidumpUTF8StringListWriter;
+ static std::string ExpectationForUTF8(const std::string& utf8) {
+ return utf8;
+ }
+ static std::string ObservationAtRVA(const std::string& file_contents,
+ RVA rva) {
+ return MinidumpUTF8StringAtRVAAsString(file_contents, rva);
+ }
+};
+
+template <typename Traits>
+void MinidumpStringListTest() {
+ std::vector<std::string> strings;
+ strings.push_back(std::string("One"));
+ strings.push_back(std::string());
+ strings.push_back(std::string("3"));
+ strings.push_back(std::string("\360\237\222\251"));
+
+ typename Traits::MinidumpStringListWriterType string_list_writer;
+ EXPECT_FALSE(string_list_writer.IsUseful());
+ string_list_writer.InitializeFromVector(strings);
+ EXPECT_TRUE(string_list_writer.IsUseful());
+
+ StringFile string_file;
+
+ ASSERT_TRUE(string_list_writer.WriteEverything(&string_file));
+
+ const MinidumpRVAList* list =
+ MinidumpRVAListAtStart(string_file.string(), strings.size());
+ ASSERT_TRUE(list);
+
+ for (size_t index = 0; index < strings.size(); ++index) {
+ EXPECT_EQ(Traits::ExpectationForUTF8(strings[index]),
+ Traits::ObservationAtRVA(string_file.string(),
+ list->children[index]));
+ }
+}
+
+TEST(MinidumpStringWriter, MinidumpUTF16StringList) {
+ MinidumpStringListTest<MinidumpUTF16StringListWriterTraits>();
+}
+
+TEST(MinidumpStringWriter, MinidumpUTF8StringList) {
+ MinidumpStringListTest<MinidumpUTF8StringListWriterTraits>();
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_system_info_writer.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_system_info_writer.cc
new file mode 100644
index 00000000000..309f3a39de6
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_system_info_writer.cc
@@ -0,0 +1,299 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_system_info_writer.h"
+
+#include <string.h>
+#include <sys/types.h>
+
+#include "base/logging.h"
+#include "minidump/minidump_string_writer.h"
+#include "snapshot/system_snapshot.h"
+#include "util/file/file_writer.h"
+
+namespace crashpad {
+
+namespace {
+
+uint64_t AMD64FeaturesFromSystemSnapshot(
+ const SystemSnapshot* system_snapshot) {
+#define ADD_FEATURE(minidump_bit) (UINT64_C(1) << (minidump_bit))
+
+ // Features for which no cpuid bits are present, but that always exist on
+ // x86_64. cmpxchg is supported on 486 and later.
+ uint64_t minidump_features = ADD_FEATURE(PF_COMPARE_EXCHANGE_DOUBLE);
+
+#define MAP_FEATURE(features, cpuid_bit, minidump_bit) \
+ do { \
+ if ((features) & (implicit_cast<decltype(features)>(1) << (cpuid_bit))) { \
+ minidump_features |= ADD_FEATURE(minidump_bit); \
+ } \
+ } while (false)
+
+#define F_TSC 4
+#define F_PAE 6
+#define F_MMX 23
+#define F_SSE 25
+#define F_SSE2 26
+#define F_SSE3 32
+#define F_CX16 45
+#define F_XSAVE 58
+#define F_RDRAND 62
+
+ uint64_t cpuid_features = system_snapshot->CPUX86Features();
+
+ MAP_FEATURE(cpuid_features, F_TSC, PF_RDTSC_INSTRUCTION_AVAILABLE);
+ MAP_FEATURE(cpuid_features, F_PAE, PF_PAE_ENABLED);
+ MAP_FEATURE(cpuid_features, F_MMX, PF_MMX_INSTRUCTIONS_AVAILABLE);
+ MAP_FEATURE(cpuid_features, F_SSE, PF_XMMI_INSTRUCTIONS_AVAILABLE);
+ MAP_FEATURE(cpuid_features, F_SSE2, PF_XMMI64_INSTRUCTIONS_AVAILABLE);
+ MAP_FEATURE(cpuid_features, F_SSE3, PF_SSE3_INSTRUCTIONS_AVAILABLE);
+ MAP_FEATURE(cpuid_features, F_CX16, PF_COMPARE_EXCHANGE128);
+ MAP_FEATURE(cpuid_features, F_XSAVE, PF_XSAVE_ENABLED);
+ MAP_FEATURE(cpuid_features, F_RDRAND, PF_RDRAND_INSTRUCTION_AVAILABLE);
+
+#define FX_XD 20
+#define FX_3DNOW 31
+
+ uint64_t extended_features = system_snapshot->CPUX86ExtendedFeatures();
+
+ MAP_FEATURE(extended_features, FX_3DNOW, PF_3DNOW_INSTRUCTIONS_AVAILABLE);
+
+#define F7_FSGSBASE 0
+
+ uint32_t leaf7_features = system_snapshot->CPUX86Leaf7Features();
+
+ MAP_FEATURE(leaf7_features, F7_FSGSBASE, PF_RDWRFSGSBASE_AVAILABLE);
+
+ // This feature bit should be set if NX (XD, DEP) is enabled, not just if
+ // it’s available on the CPU as indicated by the XF_XD bit.
+ if (system_snapshot->NXEnabled()) {
+ minidump_features |= ADD_FEATURE(PF_NX_ENABLED);
+ }
+
+ if (system_snapshot->CPUX86SupportsDAZ()) {
+ minidump_features |= ADD_FEATURE(PF_SSE_DAZ_MODE_AVAILABLE);
+ }
+
+ // PF_SECOND_LEVEL_ADDRESS_TRANSLATION can’t be determined without
+ // consulting model-specific registers, a privileged operation. The exact
+ // use of PF_VIRT_FIRMWARE_ENABLED is unknown. PF_FASTFAIL_AVAILABLE is
+ // irrelevant outside of Windows.
+
+#undef MAP_FEATURE
+#undef ADD_FEATURE
+
+ return minidump_features;
+}
+
+} // namespace
+
+MinidumpSystemInfoWriter::MinidumpSystemInfoWriter()
+ : MinidumpStreamWriter(), system_info_(), csd_version_() {
+ system_info_.ProcessorArchitecture = kMinidumpCPUArchitectureUnknown;
+}
+
+MinidumpSystemInfoWriter::~MinidumpSystemInfoWriter() {
+}
+
+void MinidumpSystemInfoWriter::InitializeFromSnapshot(
+ const SystemSnapshot* system_snapshot) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK(!csd_version_);
+
+ MinidumpCPUArchitecture cpu_architecture;
+ switch (system_snapshot->GetCPUArchitecture()) {
+ case kCPUArchitectureX86:
+ cpu_architecture = kMinidumpCPUArchitectureX86;
+ break;
+ case kCPUArchitectureX86_64:
+ cpu_architecture = kMinidumpCPUArchitectureAMD64;
+ break;
+ default:
+ NOTREACHED();
+ cpu_architecture = kMinidumpCPUArchitectureUnknown;
+ break;
+ }
+ SetCPUArchitecture(cpu_architecture);
+
+ uint32_t cpu_revision = system_snapshot->CPURevision();
+ SetCPULevelAndRevision((cpu_revision & 0xffff0000) >> 16,
+ cpu_revision & 0x0000ffff);
+ SetCPUCount(system_snapshot->CPUCount());
+
+ if (cpu_architecture == kMinidumpCPUArchitectureX86) {
+ std::string cpu_vendor = system_snapshot->CPUVendor();
+ SetCPUX86VendorString(cpu_vendor);
+
+ // The minidump file format only has room for the bottom 32 bits of CPU
+ // features and extended CPU features.
+ SetCPUX86VersionAndFeatures(system_snapshot->CPUX86Signature(),
+ system_snapshot->CPUX86Features() & 0xffffffff);
+
+ if (cpu_vendor == "AuthenticAMD") {
+ SetCPUX86AMDExtendedFeatures(
+ system_snapshot->CPUX86ExtendedFeatures() & 0xffffffff);
+ }
+ } else if (cpu_architecture == kMinidumpCPUArchitectureAMD64) {
+ SetCPUOtherFeatures(AMD64FeaturesFromSystemSnapshot(system_snapshot), 0);
+ }
+
+ MinidumpOS operating_system;
+ switch (system_snapshot->GetOperatingSystem()) {
+ case SystemSnapshot::kOperatingSystemMacOSX:
+ operating_system = kMinidumpOSMacOSX;
+ break;
+ case SystemSnapshot::kOperatingSystemWindows:
+ operating_system = kMinidumpOSWin32NT;
+ break;
+ default:
+ NOTREACHED();
+ operating_system = kMinidumpOSUnknown;
+ break;
+ }
+ SetOS(operating_system);
+
+ SetOSType(system_snapshot->OSServer() ? kMinidumpOSTypeServer
+ : kMinidumpOSTypeWorkstation);
+
+ int major;
+ int minor;
+ int bugfix;
+ std::string build;
+ system_snapshot->OSVersion(&major, &minor, &bugfix, &build);
+ SetOSVersion(major, minor, bugfix);
+ SetCSDVersion(build);
+}
+
+void MinidumpSystemInfoWriter::SetCSDVersion(const std::string& csd_version) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ if (!csd_version_) {
+ csd_version_.reset(new internal::MinidumpUTF16StringWriter());
+ }
+
+ csd_version_->SetUTF8(csd_version);
+}
+
+void MinidumpSystemInfoWriter::SetCPUX86Vendor(uint32_t ebx,
+ uint32_t edx,
+ uint32_t ecx) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK(system_info_.ProcessorArchitecture == kMinidumpCPUArchitectureX86 ||
+ system_info_.ProcessorArchitecture ==
+ kMinidumpCPUArchitectureX86Win64);
+
+ static_assert(arraysize(system_info_.Cpu.X86CpuInfo.VendorId) == 3,
+ "VendorId must have 3 elements");
+
+ system_info_.Cpu.X86CpuInfo.VendorId[0] = ebx;
+ system_info_.Cpu.X86CpuInfo.VendorId[1] = edx;
+ system_info_.Cpu.X86CpuInfo.VendorId[2] = ecx;
+}
+
+void MinidumpSystemInfoWriter::SetCPUX86VendorString(
+ const std::string& vendor) {
+ DCHECK_EQ(state(), kStateMutable);
+ CHECK_EQ(vendor.size(), sizeof(system_info_.Cpu.X86CpuInfo.VendorId));
+
+ uint32_t registers[3];
+ static_assert(
+ sizeof(registers) == sizeof(system_info_.Cpu.X86CpuInfo.VendorId),
+ "VendorId sizes must be equal");
+
+ for (size_t index = 0; index < arraysize(registers); ++index) {
+ memcpy(&registers[index],
+ &vendor[index * sizeof(*registers)],
+ sizeof(*registers));
+ }
+
+ SetCPUX86Vendor(registers[0], registers[1], registers[2]);
+}
+
+void MinidumpSystemInfoWriter::SetCPUX86VersionAndFeatures(uint32_t version,
+ uint32_t features) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK(system_info_.ProcessorArchitecture == kMinidumpCPUArchitectureX86 ||
+ system_info_.ProcessorArchitecture ==
+ kMinidumpCPUArchitectureX86Win64);
+
+ system_info_.Cpu.X86CpuInfo.VersionInformation = version;
+ system_info_.Cpu.X86CpuInfo.FeatureInformation = features;
+}
+
+void MinidumpSystemInfoWriter::SetCPUX86AMDExtendedFeatures(
+ uint32_t extended_features) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK(system_info_.ProcessorArchitecture == kMinidumpCPUArchitectureX86 ||
+ system_info_.ProcessorArchitecture ==
+ kMinidumpCPUArchitectureX86Win64);
+ DCHECK(system_info_.Cpu.X86CpuInfo.VendorId[0] == 'htuA' &&
+ system_info_.Cpu.X86CpuInfo.VendorId[1] == 'itne' &&
+ system_info_.Cpu.X86CpuInfo.VendorId[2] == 'DMAc');
+
+ system_info_.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = extended_features;
+}
+
+void MinidumpSystemInfoWriter::SetCPUOtherFeatures(uint64_t features_0,
+ uint64_t features_1) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK(system_info_.ProcessorArchitecture != kMinidumpCPUArchitectureX86 &&
+ system_info_.ProcessorArchitecture !=
+ kMinidumpCPUArchitectureX86Win64);
+
+ static_assert(arraysize(system_info_.Cpu.OtherCpuInfo.ProcessorFeatures) == 2,
+ "ProcessorFeatures must have 2 elements");
+
+ system_info_.Cpu.OtherCpuInfo.ProcessorFeatures[0] = features_0;
+ system_info_.Cpu.OtherCpuInfo.ProcessorFeatures[1] = features_1;
+}
+
+bool MinidumpSystemInfoWriter::Freeze() {
+ DCHECK_EQ(state(), kStateMutable);
+ CHECK(csd_version_);
+
+ if (!MinidumpStreamWriter::Freeze()) {
+ return false;
+ }
+
+ csd_version_->RegisterRVA(&system_info_.CSDVersionRva);
+
+ return true;
+}
+
+size_t MinidumpSystemInfoWriter::SizeOfObject() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ return sizeof(system_info_);
+}
+
+std::vector<internal::MinidumpWritable*> MinidumpSystemInfoWriter::Children() {
+ DCHECK_GE(state(), kStateFrozen);
+ DCHECK(csd_version_);
+
+ std::vector<MinidumpWritable*> children(1, csd_version_.get());
+ return children;
+}
+
+bool MinidumpSystemInfoWriter::WriteObject(FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateWritable);
+
+ return file_writer->Write(&system_info_, sizeof(system_info_));
+}
+
+MinidumpStreamType MinidumpSystemInfoWriter::StreamType() const {
+ return kMinidumpStreamTypeSystemInfo;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_system_info_writer.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_system_info_writer.h
new file mode 100644
index 00000000000..a1653dcb4c2
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_system_info_writer.h
@@ -0,0 +1,196 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_MINIDUMP_MINIDUMP_SYSTEM_INFO_WRITER_H_
+#define CRASHPAD_MINIDUMP_MINIDUMP_SYSTEM_INFO_WRITER_H_
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "minidump/minidump_extensions.h"
+#include "minidump/minidump_stream_writer.h"
+#include "minidump/minidump_writable.h"
+
+namespace crashpad {
+
+class SystemSnapshot;
+
+namespace internal {
+class MinidumpUTF16StringWriter;
+} // namespace internal
+
+//! \brief The writer for a MINIDUMP_SYSTEM_INFO stream in a minidump file.
+class MinidumpSystemInfoWriter final : public internal::MinidumpStreamWriter {
+ public:
+ MinidumpSystemInfoWriter();
+ ~MinidumpSystemInfoWriter() override;
+
+ //! \brief Initializes MINIDUMP_SYSTEM_INFO based on \a system_snapshot.
+ //!
+ //! \param[in] system_snapshot The system snapshot to use as source data.
+ //!
+ //! \note Valid in #kStateMutable. No mutator methods may be called before
+ //! this method, and it is not normally necessary to call any mutator
+ //! methods after this method.
+ void InitializeFromSnapshot(const SystemSnapshot* system_snapshot);
+
+ //! \brief Sets MINIDUMP_SYSTEM_INFO::ProcessorArchitecture.
+ void SetCPUArchitecture(MinidumpCPUArchitecture processor_architecture) {
+ system_info_.ProcessorArchitecture = processor_architecture;
+ }
+
+ //! \brief Sets MINIDUMP_SYSTEM_INFO::ProcessorLevel and
+ //! MINIDUMP_SYSTEM_INFO::ProcessorRevision.
+ void SetCPULevelAndRevision(uint16_t processor_level,
+ uint16_t processor_revision) {
+ system_info_.ProcessorLevel = processor_level;
+ system_info_.ProcessorRevision = processor_revision;
+ }
+
+ //! \brief Sets MINIDUMP_SYSTEM_INFO::NumberOfProcessors.
+ void SetCPUCount(uint8_t number_of_processors) {
+ system_info_.NumberOfProcessors = number_of_processors;
+ }
+
+ //! \brief Sets MINIDUMP_SYSTEM_INFO::PlatformId.
+ void SetOS(MinidumpOS platform_id) { system_info_.PlatformId = platform_id; }
+
+ //! \brief Sets MINIDUMP_SYSTEM_INFO::ProductType.
+ void SetOSType(MinidumpOSType product_type) {
+ system_info_.ProductType = product_type;
+ }
+
+ //! \brief Sets MINIDUMP_SYSTEM_INFO::MajorVersion,
+ //! MINIDUMP_SYSTEM_INFO::MinorVersion, and
+ //! MINIDUMP_SYSTEM_INFO::BuildNumber.
+ void SetOSVersion(uint32_t major_version,
+ uint32_t minor_version,
+ uint32_t build_number) {
+ system_info_.MajorVersion = major_version;
+ system_info_.MinorVersion = minor_version;
+ system_info_.BuildNumber = build_number;
+ }
+
+ //! \brief Arranges for MINIDUMP_SYSTEM_INFO::CSDVersionRva to point to a
+ //! MINIDUMP_STRING containing the supplied string.
+ //!
+ //! This method must be called prior to Freeze(). A CSD version is required
+ //! in all MINIDUMP_SYSTEM_INFO streams. An empty string is an acceptable
+ //! value.
+ void SetCSDVersion(const std::string& csd_version);
+
+ //! \brief Sets MINIDUMP_SYSTEM_INFO::SuiteMask.
+ void SetSuiteMask(uint16_t suite_mask) {
+ system_info_.SuiteMask = suite_mask;
+ }
+
+ //! \brief Sets \ref CPU_INFORMATION::VendorId
+ //! "MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::VendorId".
+ //!
+ //! This is only valid if SetCPUArchitecture() has been used to set the CPU
+ //! architecture to #kMinidumpCPUArchitectureX86 or
+ //! #kMinidumpCPUArchitectureX86Win64.
+ //!
+ //! \param[in] ebx The first 4 bytes of the CPU vendor string, the value
+ //! reported in `cpuid 0` `ebx`.
+ //! \param[in] edx The middle 4 bytes of the CPU vendor string, the value
+ //! reported in `cpuid 0` `edx`.
+ //! \param[in] ecx The last 4 bytes of the CPU vendor string, the value
+ //! reported by `cpuid 0` `ecx`.
+ //!
+ //! \note Do not call this method if SetCPUArchitecture() has been used to set
+ //! the CPU architecture to #kMinidumpCPUArchitectureAMD64.
+ //!
+ //! \sa SetCPUX86VendorString()
+ void SetCPUX86Vendor(uint32_t ebx, uint32_t edx, uint32_t ecx);
+
+ //! \brief Sets \ref CPU_INFORMATION::VendorId
+ //! "MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::VendorId".
+ //!
+ //! This is only valid if SetCPUArchitecture() has been used to set the CPU
+ //! architecture to #kMinidumpCPUArchitectureX86 or
+ //! #kMinidumpCPUArchitectureX86Win64.
+ //!
+ //! \param[in] vendor The entire CPU vendor string, which must be exactly 12
+ //! bytes long.
+ //!
+ //! \note Do not call this method if SetCPUArchitecture() has been used to set
+ //! the CPU architecture to #kMinidumpCPUArchitectureAMD64.
+ //!
+ //! \sa SetCPUX86Vendor()
+ void SetCPUX86VendorString(const std::string& vendor);
+
+ //! \brief Sets \ref CPU_INFORMATION::VersionInformation
+ //! "MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::VersionInformation" and
+ //! \ref CPU_INFORMATION::FeatureInformation
+ //! "MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::FeatureInformation".
+ //!
+ //! This is only valid if SetCPUArchitecture() has been used to set the CPU
+ //! architecture to #kMinidumpCPUArchitectureX86 or
+ //! #kMinidumpCPUArchitectureX86Win64.
+ //!
+ //! \note Do not call this method if SetCPUArchitecture() has been used to set
+ //! the CPU architecture to #kMinidumpCPUArchitectureAMD64.
+ void SetCPUX86VersionAndFeatures(uint32_t version, uint32_t features);
+
+ //! \brief Sets \ref CPU_INFORMATION::AMDExtendedCpuFeatures
+ //! "MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::AMDExtendedCPUFeatures".
+ //!
+ //! This is only valid if SetCPUArchitecture() has been used to set the CPU
+ //! architecture to #kMinidumpCPUArchitectureX86 or
+ //! #kMinidumpCPUArchitectureX86Win64, and if SetCPUX86Vendor() or
+ //! SetCPUX86VendorString() has been used to set the CPU vendor to
+ //! “AuthenticAMD”.
+ //!
+ //! \note Do not call this method if SetCPUArchitecture() has been used to set
+ //! the CPU architecture to #kMinidumpCPUArchitectureAMD64.
+ void SetCPUX86AMDExtendedFeatures(uint32_t extended_features);
+
+ //! \brief Sets \ref CPU_INFORMATION::ProcessorFeatures
+ //! "MINIDUMP_SYSTEM_INFO::Cpu::OtherCpuInfo::ProcessorFeatures".
+ //!
+ //! This is only valid if SetCPUArchitecture() has been used to set the CPU
+ //! architecture to an architecture other than #kMinidumpCPUArchitectureX86
+ //! or #kMinidumpCPUArchitectureX86Win64.
+ //!
+ //! \note This method may be called if SetCPUArchitecture() has been used to
+ //! set the CPU architecture to #kMinidumpCPUArchitectureAMD64.
+ void SetCPUOtherFeatures(uint64_t features_0, uint64_t features_1);
+
+ protected:
+ // MinidumpWritable:
+ bool Freeze() override;
+ size_t SizeOfObject() override;
+ std::vector<MinidumpWritable*> Children() override;
+ bool WriteObject(FileWriterInterface* file_writer) override;
+
+ // MinidumpStreamWriter:
+ MinidumpStreamType StreamType() const override;
+
+ private:
+ MINIDUMP_SYSTEM_INFO system_info_;
+ scoped_ptr<internal::MinidumpUTF16StringWriter> csd_version_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpSystemInfoWriter);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_MINIDUMP_MINIDUMP_SYSTEM_INFO_WRITER_H_
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_system_info_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_system_info_writer_test.cc
new file mode 100644
index 00000000000..daf81f60c53
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_system_info_writer_test.cc
@@ -0,0 +1,481 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_system_info_writer.h"
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <algorithm>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "gtest/gtest.h"
+#include "minidump/minidump_file_writer.h"
+#include "minidump/test/minidump_file_writer_test_util.h"
+#include "minidump/test/minidump_string_writer_test_util.h"
+#include "minidump/test/minidump_writable_test_util.h"
+#include "snapshot/test/test_system_snapshot.h"
+#include "test/gtest_death_check.h"
+#include "util/file/string_file.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+void GetSystemInfoStream(const std::string& file_contents,
+ size_t csd_version_length,
+ const MINIDUMP_SYSTEM_INFO** system_info,
+ const MINIDUMP_STRING** csd_version) {
+ // The expected number of bytes for the CSD version’s MINIDUMP_STRING::Buffer.
+ MINIDUMP_STRING tmp = {0};
+ ALLOW_UNUSED_LOCAL(tmp);
+ const size_t kCSDVersionBytes = csd_version_length * sizeof(tmp.Buffer[0]);
+ const size_t kCSDVersionBytesWithNUL =
+ kCSDVersionBytes + sizeof(tmp.Buffer[0]);
+
+ const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
+ const size_t kSystemInfoStreamOffset =
+ kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);
+ const size_t kCSDVersionOffset =
+ kSystemInfoStreamOffset + sizeof(MINIDUMP_SYSTEM_INFO);
+ const size_t kFileSize =
+ kCSDVersionOffset + sizeof(MINIDUMP_STRING) + kCSDVersionBytesWithNUL;
+
+ ASSERT_EQ(kFileSize, file_contents.size());
+
+ const MINIDUMP_DIRECTORY* directory;
+ const MINIDUMP_HEADER* header =
+ MinidumpHeaderAtStart(file_contents, &directory);
+ ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));
+ ASSERT_TRUE(directory);
+
+ ASSERT_EQ(kMinidumpStreamTypeSystemInfo, directory[0].StreamType);
+ EXPECT_EQ(kSystemInfoStreamOffset, directory[0].Location.Rva);
+
+ *system_info = MinidumpWritableAtLocationDescriptor<MINIDUMP_SYSTEM_INFO>(
+ file_contents, directory[0].Location);
+ ASSERT_TRUE(system_info);
+
+ EXPECT_EQ(kCSDVersionOffset, (*system_info)->CSDVersionRva);
+
+ *csd_version =
+ MinidumpStringAtRVA(file_contents, (*system_info)->CSDVersionRva);
+ EXPECT_EQ(kCSDVersionBytes, (*csd_version)->Length);
+}
+
+TEST(MinidumpSystemInfoWriter, Empty) {
+ MinidumpFileWriter minidump_file_writer;
+ auto system_info_writer = make_scoped_ptr(new MinidumpSystemInfoWriter());
+
+ system_info_writer->SetCSDVersion(std::string());
+
+ minidump_file_writer.AddStream(system_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_SYSTEM_INFO* system_info = nullptr;
+ const MINIDUMP_STRING* csd_version = nullptr;
+
+ ASSERT_NO_FATAL_FAILURE(
+ GetSystemInfoStream(string_file.string(), 0, &system_info, &csd_version));
+
+ EXPECT_EQ(kMinidumpCPUArchitectureUnknown,
+ system_info->ProcessorArchitecture);
+ EXPECT_EQ(0u, system_info->ProcessorLevel);
+ EXPECT_EQ(0u, system_info->ProcessorRevision);
+ EXPECT_EQ(0u, system_info->NumberOfProcessors);
+ EXPECT_EQ(0u, system_info->ProductType);
+ EXPECT_EQ(0u, system_info->MajorVersion);
+ EXPECT_EQ(0u, system_info->MinorVersion);
+ EXPECT_EQ(0u, system_info->BuildNumber);
+ EXPECT_EQ(0u, system_info->PlatformId);
+ EXPECT_EQ(0u, system_info->SuiteMask);
+ EXPECT_EQ(0u, system_info->Cpu.X86CpuInfo.VendorId[0]);
+ EXPECT_EQ(0u, system_info->Cpu.X86CpuInfo.VendorId[1]);
+ EXPECT_EQ(0u, system_info->Cpu.X86CpuInfo.VendorId[2]);
+ EXPECT_EQ(0u, system_info->Cpu.X86CpuInfo.VersionInformation);
+ EXPECT_EQ(0u, system_info->Cpu.X86CpuInfo.FeatureInformation);
+ EXPECT_EQ(0u, system_info->Cpu.X86CpuInfo.AMDExtendedCpuFeatures);
+ EXPECT_EQ(0u, system_info->Cpu.OtherCpuInfo.ProcessorFeatures[0]);
+ EXPECT_EQ(0u, system_info->Cpu.OtherCpuInfo.ProcessorFeatures[1]);
+
+ EXPECT_EQ('\0', csd_version->Buffer[0]);
+}
+
+TEST(MinidumpSystemInfoWriter, X86_Win) {
+ MinidumpFileWriter minidump_file_writer;
+ auto system_info_writer = make_scoped_ptr(new MinidumpSystemInfoWriter());
+
+ const MinidumpCPUArchitecture kCPUArchitecture = kMinidumpCPUArchitectureX86;
+ const uint16_t kCPULevel = 0x0010;
+ const uint16_t kCPURevision = 0x0602;
+ const uint8_t kCPUCount = 1;
+ const MinidumpOS kOS = kMinidumpOSWin32NT;
+ const MinidumpOSType kOSType = kMinidumpOSTypeWorkstation;
+ const uint32_t kOSVersionMajor = 6;
+ const uint32_t kOSVersionMinor = 1;
+ const uint32_t kOSVersionBuild = 7601;
+ const char kCSDVersion[] = "Service Pack 1";
+ const uint16_t kSuiteMask = VER_SUITE_SINGLEUSERTS;
+ const char kCPUVendor[] = "AuthenticAMD";
+ const uint32_t kCPUVersion = 0x00100f62;
+ const uint32_t kCPUFeatures = 0x078bfbff;
+ const uint32_t kAMDFeatures = 0xefd3fbff;
+
+ uint32_t cpu_vendor_registers[3];
+ ASSERT_EQ(sizeof(cpu_vendor_registers), strlen(kCPUVendor));
+ memcpy(cpu_vendor_registers, kCPUVendor, sizeof(cpu_vendor_registers));
+
+ system_info_writer->SetCPUArchitecture(kCPUArchitecture);
+ system_info_writer->SetCPULevelAndRevision(kCPULevel, kCPURevision);
+ system_info_writer->SetCPUCount(kCPUCount);
+ system_info_writer->SetOS(kOS);
+ system_info_writer->SetOSType(kMinidumpOSTypeWorkstation);
+ system_info_writer->SetOSVersion(
+ kOSVersionMajor, kOSVersionMinor, kOSVersionBuild);
+ system_info_writer->SetCSDVersion(kCSDVersion);
+ system_info_writer->SetSuiteMask(kSuiteMask);
+ system_info_writer->SetCPUX86VendorString(kCPUVendor);
+ system_info_writer->SetCPUX86VersionAndFeatures(kCPUVersion, kCPUFeatures);
+ system_info_writer->SetCPUX86AMDExtendedFeatures(kAMDFeatures);
+
+ minidump_file_writer.AddStream(system_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_SYSTEM_INFO* system_info = nullptr;
+ const MINIDUMP_STRING* csd_version = nullptr;
+
+ ASSERT_NO_FATAL_FAILURE(GetSystemInfoStream(
+ string_file.string(), strlen(kCSDVersion), &system_info, &csd_version));
+
+ EXPECT_EQ(kCPUArchitecture, system_info->ProcessorArchitecture);
+ EXPECT_EQ(kCPULevel, system_info->ProcessorLevel);
+ EXPECT_EQ(kCPURevision, system_info->ProcessorRevision);
+ EXPECT_EQ(kCPUCount, system_info->NumberOfProcessors);
+ EXPECT_EQ(kOSType, system_info->ProductType);
+ EXPECT_EQ(kOSVersionMajor, system_info->MajorVersion);
+ EXPECT_EQ(kOSVersionMinor, system_info->MinorVersion);
+ EXPECT_EQ(kOSVersionBuild, system_info->BuildNumber);
+ EXPECT_EQ(kOS, system_info->PlatformId);
+ EXPECT_EQ(kSuiteMask, system_info->SuiteMask);
+ EXPECT_EQ(cpu_vendor_registers[0], system_info->Cpu.X86CpuInfo.VendorId[0]);
+ EXPECT_EQ(cpu_vendor_registers[1], system_info->Cpu.X86CpuInfo.VendorId[1]);
+ EXPECT_EQ(cpu_vendor_registers[2], system_info->Cpu.X86CpuInfo.VendorId[2]);
+ EXPECT_EQ(kCPUVersion, system_info->Cpu.X86CpuInfo.VersionInformation);
+ EXPECT_EQ(kCPUFeatures, system_info->Cpu.X86CpuInfo.FeatureInformation);
+ EXPECT_EQ(kAMDFeatures, system_info->Cpu.X86CpuInfo.AMDExtendedCpuFeatures);
+
+ for (size_t index = 0; index < strlen(kCSDVersion); ++index) {
+ EXPECT_EQ(kCSDVersion[index], csd_version->Buffer[index]) << index;
+ }
+}
+
+TEST(MinidumpSystemInfoWriter, AMD64_Mac) {
+ MinidumpFileWriter minidump_file_writer;
+ auto system_info_writer = make_scoped_ptr(new MinidumpSystemInfoWriter());
+
+ const MinidumpCPUArchitecture kCPUArchitecture =
+ kMinidumpCPUArchitectureAMD64;
+ const uint16_t kCPULevel = 0x0006;
+ const uint16_t kCPURevision = 0x3a09;
+ const uint8_t kCPUCount = 8;
+ const MinidumpOS kOS = kMinidumpOSMacOSX;
+ const MinidumpOSType kOSType = kMinidumpOSTypeWorkstation;
+ const uint32_t kOSVersionMajor = 10;
+ const uint32_t kOSVersionMinor = 9;
+ const uint32_t kOSVersionBuild = 4;
+ const char kCSDVersion[] = "13E28";
+ const uint64_t kCPUFeatures[2] = {0x10427f4c, 0x00000000};
+
+ system_info_writer->SetCPUArchitecture(kCPUArchitecture);
+ system_info_writer->SetCPULevelAndRevision(kCPULevel, kCPURevision);
+ system_info_writer->SetCPUCount(kCPUCount);
+ system_info_writer->SetOS(kOS);
+ system_info_writer->SetOSType(kMinidumpOSTypeWorkstation);
+ system_info_writer->SetOSVersion(
+ kOSVersionMajor, kOSVersionMinor, kOSVersionBuild);
+ system_info_writer->SetCSDVersion(kCSDVersion);
+ system_info_writer->SetCPUOtherFeatures(kCPUFeatures[0], kCPUFeatures[1]);
+
+ minidump_file_writer.AddStream(system_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_SYSTEM_INFO* system_info = nullptr;
+ const MINIDUMP_STRING* csd_version;
+
+ ASSERT_NO_FATAL_FAILURE(GetSystemInfoStream(
+ string_file.string(), strlen(kCSDVersion), &system_info, &csd_version));
+
+ EXPECT_EQ(kCPUArchitecture, system_info->ProcessorArchitecture);
+ EXPECT_EQ(kCPULevel, system_info->ProcessorLevel);
+ EXPECT_EQ(kCPURevision, system_info->ProcessorRevision);
+ EXPECT_EQ(kCPUCount, system_info->NumberOfProcessors);
+ EXPECT_EQ(kOSType, system_info->ProductType);
+ EXPECT_EQ(kOSVersionMajor, system_info->MajorVersion);
+ EXPECT_EQ(kOSVersionMinor, system_info->MinorVersion);
+ EXPECT_EQ(kOSVersionBuild, system_info->BuildNumber);
+ EXPECT_EQ(kOS, system_info->PlatformId);
+ EXPECT_EQ(0u, system_info->SuiteMask);
+ EXPECT_EQ(kCPUFeatures[0],
+ system_info->Cpu.OtherCpuInfo.ProcessorFeatures[0]);
+ EXPECT_EQ(kCPUFeatures[1],
+ system_info->Cpu.OtherCpuInfo.ProcessorFeatures[1]);
+}
+
+TEST(MinidumpSystemInfoWriter, X86_CPUVendorFromRegisters) {
+ // MinidumpSystemInfoWriter.X86_Win already tested SetCPUX86VendorString().
+ // This test exercises SetCPUX86Vendor() to set the vendor from register
+ // values.
+ MinidumpFileWriter minidump_file_writer;
+ auto system_info_writer = make_scoped_ptr(new MinidumpSystemInfoWriter());
+
+ const MinidumpCPUArchitecture kCPUArchitecture = kMinidumpCPUArchitectureX86;
+ const uint32_t kCPUVendor[] = {'uneG', 'Ieni', 'letn'};
+
+ system_info_writer->SetCPUArchitecture(kCPUArchitecture);
+ system_info_writer->SetCPUX86Vendor(
+ kCPUVendor[0], kCPUVendor[1], kCPUVendor[2]);
+ system_info_writer->SetCSDVersion(std::string());
+
+ minidump_file_writer.AddStream(system_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_SYSTEM_INFO* system_info = nullptr;
+ const MINIDUMP_STRING* csd_version;
+
+ ASSERT_NO_FATAL_FAILURE(
+ GetSystemInfoStream(string_file.string(), 0, &system_info, &csd_version));
+
+ EXPECT_EQ(kCPUArchitecture, system_info->ProcessorArchitecture);
+ EXPECT_EQ(0u, system_info->ProcessorLevel);
+ EXPECT_EQ(kCPUVendor[0], system_info->Cpu.X86CpuInfo.VendorId[0]);
+ EXPECT_EQ(kCPUVendor[1], system_info->Cpu.X86CpuInfo.VendorId[1]);
+ EXPECT_EQ(kCPUVendor[2], system_info->Cpu.X86CpuInfo.VendorId[2]);
+ EXPECT_EQ(0u, system_info->Cpu.X86CpuInfo.VersionInformation);
+}
+
+TEST(MinidumpSystemInfoWriter, InitializeFromSnapshot_X86) {
+ MINIDUMP_SYSTEM_INFO expect_system_info = {};
+
+ const uint16_t kCPUFamily = 6;
+ const uint8_t kCPUModel = 70;
+ const uint8_t kCPUStepping = 1;
+
+ const uint8_t kCPUBasicFamily =
+ static_cast<uint8_t>(std::min(kCPUFamily, static_cast<uint16_t>(15)));
+ const uint8_t kCPUExtendedFamily = kCPUFamily - kCPUBasicFamily;
+
+ // These checks ensure that even if the constants above change, they represent
+ // something that can legitimately be encoded in the form used by cpuid 1 eax.
+ EXPECT_LE(kCPUFamily, 270);
+ EXPECT_LE(kCPUStepping, 15);
+ EXPECT_TRUE(kCPUBasicFamily == 6 || kCPUBasicFamily == 15 || kCPUModel <= 15);
+
+ const uint8_t kCPUBasicModel = kCPUModel & 0xf;
+ const uint8_t kCPUExtendedModel = kCPUModel >> 4;
+ const uint32_t kCPUSignature =
+ (kCPUExtendedFamily << 20) | (kCPUExtendedModel << 16) |
+ (kCPUBasicFamily << 8) | (kCPUBasicModel << 4) | kCPUStepping;
+ const uint64_t kCPUX86Features = 0x7ffafbffbfebfbff;
+ expect_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureX86;
+ expect_system_info.ProcessorLevel = kCPUFamily;
+ expect_system_info.ProcessorRevision = (kCPUModel << 8) | kCPUStepping;
+ expect_system_info.NumberOfProcessors = 8;
+ expect_system_info.ProductType = kMinidumpOSTypeServer;
+ expect_system_info.MajorVersion = 10;
+ expect_system_info.MinorVersion = 9;
+ expect_system_info.BuildNumber = 5;
+ expect_system_info.PlatformId = kMinidumpOSMacOSX;
+ expect_system_info.SuiteMask = 0;
+ expect_system_info.Cpu.X86CpuInfo.VendorId[0] = 'uneG';
+ expect_system_info.Cpu.X86CpuInfo.VendorId[1] = 'Ieni';
+ expect_system_info.Cpu.X86CpuInfo.VendorId[2] = 'letn';
+ expect_system_info.Cpu.X86CpuInfo.VersionInformation = kCPUSignature;
+ expect_system_info.Cpu.X86CpuInfo.FeatureInformation =
+ kCPUX86Features & 0xffffffff;
+ const char kCPUVendor[] = "GenuineIntel";
+ const char kOSVersionBuild[] = "13F34";
+
+ TestSystemSnapshot system_snapshot;
+ system_snapshot.SetCPUArchitecture(kCPUArchitectureX86);
+ system_snapshot.SetCPURevision(
+ (kCPUFamily << 16) | (kCPUModel << 8) | kCPUStepping);
+ system_snapshot.SetCPUCount(expect_system_info.NumberOfProcessors);
+ system_snapshot.SetCPUVendor(kCPUVendor);
+ system_snapshot.SetCPUX86Signature(kCPUSignature);
+ system_snapshot.SetCPUX86Features(kCPUX86Features);
+ system_snapshot.SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX);
+ system_snapshot.SetOSServer(true);
+ system_snapshot.SetOSVersion(expect_system_info.MajorVersion,
+ expect_system_info.MinorVersion,
+ expect_system_info.BuildNumber,
+ kOSVersionBuild);
+
+ auto system_info_writer = make_scoped_ptr(new MinidumpSystemInfoWriter());
+ system_info_writer->InitializeFromSnapshot(&system_snapshot);
+
+ MinidumpFileWriter minidump_file_writer;
+ minidump_file_writer.AddStream(system_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_SYSTEM_INFO* system_info = nullptr;
+ const MINIDUMP_STRING* csd_version = nullptr;
+ ASSERT_NO_FATAL_FAILURE(GetSystemInfoStream(string_file.string(),
+ strlen(kOSVersionBuild),
+ &system_info,
+ &csd_version));
+
+ EXPECT_EQ(expect_system_info.ProcessorArchitecture,
+ system_info->ProcessorArchitecture);
+ EXPECT_EQ(expect_system_info.ProcessorLevel, system_info->ProcessorLevel);
+ EXPECT_EQ(expect_system_info.ProcessorRevision,
+ system_info->ProcessorRevision);
+ EXPECT_EQ(expect_system_info.NumberOfProcessors,
+ system_info->NumberOfProcessors);
+ EXPECT_EQ(expect_system_info.ProductType, system_info->ProductType);
+ EXPECT_EQ(expect_system_info.MajorVersion, system_info->MajorVersion);
+ EXPECT_EQ(expect_system_info.MinorVersion, system_info->MinorVersion);
+ EXPECT_EQ(expect_system_info.BuildNumber, system_info->BuildNumber);
+ EXPECT_EQ(expect_system_info.PlatformId, system_info->PlatformId);
+ EXPECT_EQ(expect_system_info.SuiteMask, system_info->SuiteMask);
+ EXPECT_EQ(expect_system_info.Cpu.X86CpuInfo.VendorId[0],
+ system_info->Cpu.X86CpuInfo.VendorId[0]);
+ EXPECT_EQ(expect_system_info.Cpu.X86CpuInfo.VendorId[1],
+ system_info->Cpu.X86CpuInfo.VendorId[1]);
+ EXPECT_EQ(expect_system_info.Cpu.X86CpuInfo.VendorId[2],
+ system_info->Cpu.X86CpuInfo.VendorId[2]);
+ EXPECT_EQ(expect_system_info.Cpu.X86CpuInfo.VersionInformation,
+ system_info->Cpu.X86CpuInfo.VersionInformation);
+ EXPECT_EQ(expect_system_info.Cpu.X86CpuInfo.FeatureInformation,
+ system_info->Cpu.X86CpuInfo.FeatureInformation);
+
+ for (size_t index = 0; index < strlen(kOSVersionBuild); ++index) {
+ EXPECT_EQ(kOSVersionBuild[index], csd_version->Buffer[index]) << index;
+ }
+}
+
+TEST(MinidumpSystemInfoWriter, InitializeFromSnapshot_AMD64) {
+ MINIDUMP_SYSTEM_INFO expect_system_info = {};
+
+ const uint8_t kCPUFamily = 6;
+ const uint8_t kCPUModel = 70;
+ const uint8_t kCPUStepping = 1;
+ expect_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureAMD64;
+ expect_system_info.ProcessorLevel = kCPUFamily;
+ expect_system_info.ProcessorRevision = (kCPUModel << 8) | kCPUStepping;
+ expect_system_info.NumberOfProcessors = 8;
+ expect_system_info.ProductType = kMinidumpOSTypeServer;
+ expect_system_info.MajorVersion = 10;
+ expect_system_info.MinorVersion = 9;
+ expect_system_info.BuildNumber = 5;
+ expect_system_info.PlatformId = kMinidumpOSMacOSX;
+ expect_system_info.SuiteMask = 0;
+ expect_system_info.Cpu.OtherCpuInfo.ProcessorFeatures[0] =
+ (1 << PF_COMPARE_EXCHANGE_DOUBLE) |
+ (1 << PF_MMX_INSTRUCTIONS_AVAILABLE) |
+ (1 << PF_XMMI_INSTRUCTIONS_AVAILABLE) |
+ (1 << PF_RDTSC_INSTRUCTION_AVAILABLE) |
+ (1 << PF_PAE_ENABLED) |
+ (1 << PF_XMMI64_INSTRUCTIONS_AVAILABLE) |
+ (1 << PF_SSE_DAZ_MODE_AVAILABLE) |
+ (1 << PF_NX_ENABLED) |
+ (1 << PF_SSE3_INSTRUCTIONS_AVAILABLE) |
+ (1 << PF_COMPARE_EXCHANGE128) |
+ (1 << PF_XSAVE_ENABLED) |
+ (1 << PF_RDWRFSGSBASE_AVAILABLE) |
+ (1 << PF_RDRAND_INSTRUCTION_AVAILABLE);
+ expect_system_info.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0;
+ const char kOSVersionBuild[] = "13F34";
+
+ TestSystemSnapshot system_snapshot;
+ system_snapshot.SetCPUArchitecture(kCPUArchitectureX86_64);
+ system_snapshot.SetCPURevision(
+ (kCPUFamily << 16) | (kCPUModel << 8) | kCPUStepping);
+ system_snapshot.SetCPUCount(expect_system_info.NumberOfProcessors);
+ system_snapshot.SetCPUX86Features(0x7ffafbffbfebfbff);
+ system_snapshot.SetCPUX86ExtendedFeatures(0x000000212c100900);
+ system_snapshot.SetCPUX86Leaf7Features(0x00002fbb);
+ system_snapshot.SetCPUX86SupportsDAZ(true);
+ system_snapshot.SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX);
+ system_snapshot.SetOSServer(true);
+ system_snapshot.SetOSVersion(expect_system_info.MajorVersion,
+ expect_system_info.MinorVersion,
+ expect_system_info.BuildNumber,
+ kOSVersionBuild);
+ system_snapshot.SetNXEnabled(true);
+
+ auto system_info_writer = make_scoped_ptr(new MinidumpSystemInfoWriter());
+ system_info_writer->InitializeFromSnapshot(&system_snapshot);
+
+ MinidumpFileWriter minidump_file_writer;
+ minidump_file_writer.AddStream(system_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_SYSTEM_INFO* system_info = nullptr;
+ const MINIDUMP_STRING* csd_version = nullptr;
+ ASSERT_NO_FATAL_FAILURE(GetSystemInfoStream(string_file.string(),
+ strlen(kOSVersionBuild),
+ &system_info,
+ &csd_version));
+
+ EXPECT_EQ(expect_system_info.ProcessorArchitecture,
+ system_info->ProcessorArchitecture);
+ EXPECT_EQ(expect_system_info.ProcessorLevel, system_info->ProcessorLevel);
+ EXPECT_EQ(expect_system_info.ProcessorRevision,
+ system_info->ProcessorRevision);
+ EXPECT_EQ(expect_system_info.NumberOfProcessors,
+ system_info->NumberOfProcessors);
+ EXPECT_EQ(expect_system_info.ProductType, system_info->ProductType);
+ EXPECT_EQ(expect_system_info.MajorVersion, system_info->MajorVersion);
+ EXPECT_EQ(expect_system_info.MinorVersion, system_info->MinorVersion);
+ EXPECT_EQ(expect_system_info.BuildNumber, system_info->BuildNumber);
+ EXPECT_EQ(expect_system_info.PlatformId, system_info->PlatformId);
+ EXPECT_EQ(expect_system_info.SuiteMask, system_info->SuiteMask);
+ EXPECT_EQ(expect_system_info.Cpu.OtherCpuInfo.ProcessorFeatures[0],
+ system_info->Cpu.OtherCpuInfo.ProcessorFeatures[0]);
+ EXPECT_EQ(expect_system_info.Cpu.OtherCpuInfo.ProcessorFeatures[1],
+ system_info->Cpu.OtherCpuInfo.ProcessorFeatures[1]);
+
+ for (size_t index = 0; index < strlen(kOSVersionBuild); ++index) {
+ EXPECT_EQ(kOSVersionBuild[index], csd_version->Buffer[index]) << index;
+ }
+}
+
+TEST(MinidumpSystemInfoWriterDeathTest, NoCSDVersion) {
+ MinidumpFileWriter minidump_file_writer;
+ auto system_info_writer = make_scoped_ptr(new MinidumpSystemInfoWriter());
+ minidump_file_writer.AddStream(system_info_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_DEATH_CHECK(minidump_file_writer.WriteEverything(&string_file),
+ "csd_version_");
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_test.gyp b/chromium/third_party/crashpad/crashpad/minidump/minidump_test.gyp
new file mode 100644
index 00000000000..35b7d1a9f76
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_test.gyp
@@ -0,0 +1,66 @@
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'includes': [
+ '../build/crashpad.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'crashpad_minidump_test',
+ 'type': 'executable',
+ 'dependencies': [
+ 'minidump.gyp:crashpad_minidump',
+ '../snapshot/snapshot_test.gyp:crashpad_snapshot_test_lib',
+ '../test/test.gyp:crashpad_test',
+ '../third_party/gtest/gtest.gyp:gtest',
+ '../third_party/gtest/gtest.gyp:gtest_main',
+ '../third_party/mini_chromium/mini_chromium.gyp:base',
+ '../util/util.gyp:crashpad_util',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'minidump_context_writer_test.cc',
+ 'minidump_crashpad_info_writer_test.cc',
+ 'minidump_exception_writer_test.cc',
+ 'minidump_file_writer_test.cc',
+ 'minidump_memory_writer_test.cc',
+ 'minidump_misc_info_writer_test.cc',
+ 'minidump_module_crashpad_info_writer_test.cc',
+ 'minidump_module_writer_test.cc',
+ 'minidump_rva_list_writer_test.cc',
+ 'minidump_simple_string_dictionary_writer_test.cc',
+ 'minidump_string_writer_test.cc',
+ 'minidump_system_info_writer_test.cc',
+ 'minidump_thread_id_map_test.cc',
+ 'minidump_thread_writer_test.cc',
+ 'minidump_writable_test.cc',
+ 'test/minidump_context_test_util.cc',
+ 'test/minidump_context_test_util.h',
+ 'test/minidump_file_writer_test_util.cc',
+ 'test/minidump_file_writer_test_util.h',
+ 'test/minidump_memory_writer_test_util.cc',
+ 'test/minidump_memory_writer_test_util.h',
+ 'test/minidump_rva_list_test_util.cc',
+ 'test/minidump_rva_list_test_util.h',
+ 'test/minidump_string_writer_test_util.cc',
+ 'test/minidump_string_writer_test_util.h',
+ 'test/minidump_writable_test_util.cc',
+ 'test/minidump_writable_test_util.h',
+ ],
+ },
+ ],
+}
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_id_map.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_id_map.cc
new file mode 100644
index 00000000000..327d12e1352
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_id_map.cc
@@ -0,0 +1,67 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_thread_id_map.h"
+
+#include <limits>
+#include <set>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "snapshot/thread_snapshot.h"
+
+namespace crashpad {
+
+void BuildMinidumpThreadIDMap(
+ const std::vector<const ThreadSnapshot*>& thread_snapshots,
+ MinidumpThreadIDMap* thread_id_map) {
+ DCHECK(thread_id_map->empty());
+
+ // First, try truncating each 64-bit thread ID to 32 bits. If that’s possible
+ // for each unique 64-bit thread ID, then this will be used as the mapping.
+ // This preserves as much of the original thread ID as possible when feasible.
+ bool collision = false;
+ std::set<uint32_t> thread_ids_32;
+ for (const ThreadSnapshot* thread_snapshot : thread_snapshots) {
+ uint64_t thread_id_64 = thread_snapshot->ThreadID();
+ if (thread_id_map->find(thread_id_64) == thread_id_map->end()) {
+ uint32_t thread_id_32 = static_cast<uint32_t>(thread_id_64);
+ if (!thread_ids_32.insert(thread_id_32).second) {
+ collision = true;
+ break;
+ }
+ thread_id_map->insert(std::make_pair(thread_id_64, thread_id_32));
+ }
+ }
+
+ if (collision) {
+ // Since there was a collision, go back and assign each unique 64-bit thread
+ // ID its own sequential 32-bit equivalent. The 32-bit thread IDs will not
+ // bear any resemblance to the original 64-bit thread IDs.
+ thread_id_map->clear();
+ for (const ThreadSnapshot* thread_snapshot : thread_snapshots) {
+ uint64_t thread_id_64 = thread_snapshot->ThreadID();
+ if (thread_id_map->find(thread_id_64) == thread_id_map->end()) {
+ uint32_t thread_id_32 =
+ base::checked_cast<uint32_t>(thread_id_map->size());
+ thread_id_map->insert(std::make_pair(thread_id_64, thread_id_32));
+ }
+ }
+
+ DCHECK_LE(thread_id_map->size(), std::numeric_limits<uint32_t>::max());
+ }
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_id_map.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_id_map.h
new file mode 100644
index 00000000000..33b105fba19
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_id_map.h
@@ -0,0 +1,52 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_MINIDUMP_MINIDUMP_THREAD_ID_MAP_H_
+#define CRASHPAD_MINIDUMP_MINIDUMP_THREAD_ID_MAP_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <vector>
+
+namespace crashpad {
+
+class ThreadSnapshot;
+
+//! \brief A map that connects 64-bit snapshot thread IDs to 32-bit minidump
+//! thread IDs.
+//!
+//! 64-bit snapshot thread IDs are obtained from ThreadSnapshot::ThreadID().
+//! 32-bit minidump thread IDs are stored in MINIDUMP_THREAD::ThreadId.
+//!
+//! A ThreadIDMap ensures that there are no collisions among the set of 32-bit
+//! minidump thread IDs.
+using MinidumpThreadIDMap = std::map<uint64_t, uint32_t>;
+
+//! \brief Builds a MinidumpThreadIDMap for a group of ThreadSnapshot objects.
+//!
+//! \param[in] thread_snapshots The thread snapshots to use as source data.
+//! \param[out] thread_id_map A MinidumpThreadIDMap to be built by this method.
+//! This map must be empty when this function is called.
+//!
+//! The map ensures that for any unique 64-bit thread ID found in a
+//! ThreadSnapshot, the 32-bit thread ID used in a minidump file will also be
+//! unique.
+void BuildMinidumpThreadIDMap(
+ const std::vector<const ThreadSnapshot*>& thread_snapshots,
+ MinidumpThreadIDMap* thread_id_map);
+
+} // namespace crashpad
+
+#endif // CRASHPAD_MINIDUMP_MINIDUMP_THREAD_ID_MAP_H_
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_id_map_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_id_map_test.cc
new file mode 100644
index 00000000000..058709e981a
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_id_map_test.cc
@@ -0,0 +1,190 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_thread_id_map.h"
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "gtest/gtest.h"
+#include "snapshot/test/test_thread_snapshot.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+class MinidumpThreadIDMapTest : public testing::Test {
+ public:
+ MinidumpThreadIDMapTest()
+ : Test(),
+ thread_snapshots_(),
+ test_thread_snapshots_() {
+ }
+
+ ~MinidumpThreadIDMapTest() override {}
+
+ // testing::Test:
+ void SetUp() override {
+ for (size_t index = 0; index < arraysize(test_thread_snapshots_); ++index) {
+ thread_snapshots_.push_back(&test_thread_snapshots_[index]);
+ }
+ }
+
+ protected:
+ static bool MapHasKeyValue(
+ const MinidumpThreadIDMap* map, uint64_t key, uint32_t expected_value) {
+ auto iterator = map->find(key);
+ if (iterator == map->end()) {
+ EXPECT_NE(map->end(), iterator);
+ return false;
+ }
+ if (iterator->second != expected_value) {
+ EXPECT_EQ(expected_value, iterator->second);
+ return false;
+ }
+ return true;
+ }
+
+ void SetThreadID(size_t index, uint64_t thread_id) {
+ ASSERT_LT(index, arraysize(test_thread_snapshots_));
+ test_thread_snapshots_[index].SetThreadID(thread_id);
+ }
+
+ const std::vector<const ThreadSnapshot*>& thread_snapshots() const {
+ return thread_snapshots_;
+ }
+
+ private:
+ std::vector<const ThreadSnapshot*> thread_snapshots_;
+ TestThreadSnapshot test_thread_snapshots_[5];
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpThreadIDMapTest);
+};
+
+TEST_F(MinidumpThreadIDMapTest, NoThreads) {
+ // Don’t use thread_snapshots(), because it’s got some threads in it, and the
+ // point of this test is to make sure that BuildMinidumpThreadIDMap() works
+ // with no threads.
+ std::vector<const ThreadSnapshot*> thread_snapshots;
+ MinidumpThreadIDMap thread_id_map;
+ BuildMinidumpThreadIDMap(thread_snapshots, &thread_id_map);
+
+ EXPECT_TRUE(thread_id_map.empty());
+}
+
+TEST_F(MinidumpThreadIDMapTest, SimpleMapping) {
+ SetThreadID(0, 1);
+ SetThreadID(1, 3);
+ SetThreadID(2, 5);
+ SetThreadID(3, 7);
+ SetThreadID(4, 9);
+
+ MinidumpThreadIDMap thread_id_map;
+ BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map);
+
+ EXPECT_EQ(5u, thread_id_map.size());
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 1, 1);
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 3, 3);
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 5, 5);
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 7, 7);
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 9, 9);
+}
+
+TEST_F(MinidumpThreadIDMapTest, Truncation) {
+ SetThreadID(0, 0x0000000000000000);
+ SetThreadID(1, 0x9999999900000001);
+ SetThreadID(2, 0x9999999980000001);
+ SetThreadID(3, 0x99999999fffffffe);
+ SetThreadID(4, 0x99999999ffffffff);
+
+ MinidumpThreadIDMap thread_id_map;
+ BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map);
+
+ EXPECT_EQ(5u, thread_id_map.size());
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000000, 0x00000000);
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x9999999900000001, 0x00000001);
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x9999999980000001, 0x80000001);
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x99999999fffffffe, 0xfffffffe);
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x99999999ffffffff, 0xffffffff);
+}
+
+TEST_F(MinidumpThreadIDMapTest, DuplicateThreadID) {
+ SetThreadID(0, 2);
+ SetThreadID(1, 4);
+ SetThreadID(2, 4);
+ SetThreadID(3, 6);
+ SetThreadID(4, 8);
+
+ MinidumpThreadIDMap thread_id_map;
+ BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map);
+
+ EXPECT_EQ(4u, thread_id_map.size());
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 2, 2);
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 4, 4);
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 6, 6);
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 8, 8);
+}
+
+TEST_F(MinidumpThreadIDMapTest, Collision) {
+ SetThreadID(0, 0x0000000000000010);
+ SetThreadID(1, 0x0000000000000020);
+ SetThreadID(2, 0x0000000000000030);
+ SetThreadID(3, 0x0000000000000040);
+ SetThreadID(4, 0x0000000100000010);
+
+ MinidumpThreadIDMap thread_id_map;
+ BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map);
+
+ EXPECT_EQ(5u, thread_id_map.size());
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000010, 0);
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000020, 1);
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000030, 2);
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000040, 3);
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000100000010, 4);
+}
+
+TEST_F(MinidumpThreadIDMapTest, DuplicateAndCollision) {
+ SetThreadID(0, 0x0000000100000010);
+ SetThreadID(1, 0x0000000000000010);
+ SetThreadID(2, 0x0000000000000020);
+ SetThreadID(3, 0x0000000000000030);
+ SetThreadID(4, 0x0000000000000020);
+
+ MinidumpThreadIDMap thread_id_map;
+ BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map);
+
+ EXPECT_EQ(4u, thread_id_map.size());
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000100000010, 0);
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000010, 1);
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000020, 2);
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000030, 3);
+}
+
+TEST_F(MinidumpThreadIDMapTest, AllDuplicates) {
+ SetThreadID(0, 6);
+ SetThreadID(1, 6);
+ SetThreadID(2, 6);
+ SetThreadID(3, 6);
+ SetThreadID(4, 6);
+
+ MinidumpThreadIDMap thread_id_map;
+ BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map);
+
+ EXPECT_EQ(1u, thread_id_map.size());
+ EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 6, 6);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer.cc
new file mode 100644
index 00000000000..a36d7614426
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer.cc
@@ -0,0 +1,231 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_thread_writer.h"
+
+#include <sys/types.h>
+
+#include "base/logging.h"
+#include "minidump/minidump_context_writer.h"
+#include "minidump/minidump_memory_writer.h"
+#include "snapshot/memory_snapshot.h"
+#include "snapshot/thread_snapshot.h"
+#include "util/file/file_writer.h"
+#include "util/numeric/safe_assignment.h"
+
+namespace crashpad {
+
+MinidumpThreadWriter::MinidumpThreadWriter()
+ : MinidumpWritable(), thread_(), stack_(nullptr), context_(nullptr) {
+}
+
+MinidumpThreadWriter::~MinidumpThreadWriter() {
+}
+
+void MinidumpThreadWriter::InitializeFromSnapshot(
+ const ThreadSnapshot* thread_snapshot,
+ const MinidumpThreadIDMap* thread_id_map) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK(!stack_);
+ DCHECK(!context_);
+
+ auto thread_id_it = thread_id_map->find(thread_snapshot->ThreadID());
+ DCHECK(thread_id_it != thread_id_map->end());
+ SetThreadID(thread_id_it->second);
+
+ SetSuspendCount(thread_snapshot->SuspendCount());
+ SetPriority(thread_snapshot->Priority());
+ SetTEB(thread_snapshot->ThreadSpecificDataAddress());
+
+ const MemorySnapshot* stack_snapshot = thread_snapshot->Stack();
+ if (stack_snapshot && stack_snapshot->Size() > 0) {
+ scoped_ptr<MinidumpMemoryWriter> stack =
+ MinidumpMemoryWriter::CreateFromSnapshot(stack_snapshot);
+ SetStack(stack.Pass());
+ }
+
+ scoped_ptr<MinidumpContextWriter> context =
+ MinidumpContextWriter::CreateFromSnapshot(thread_snapshot->Context());
+ SetContext(context.Pass());
+}
+
+const MINIDUMP_THREAD* MinidumpThreadWriter::MinidumpThread() const {
+ DCHECK_EQ(state(), kStateWritable);
+
+ return &thread_;
+}
+
+void MinidumpThreadWriter::SetStack(scoped_ptr<MinidumpMemoryWriter> stack) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ stack_ = stack.Pass();
+}
+
+void MinidumpThreadWriter::SetContext(
+ scoped_ptr<MinidumpContextWriter> context) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ context_ = context.Pass();
+}
+
+bool MinidumpThreadWriter::Freeze() {
+ DCHECK_EQ(state(), kStateMutable);
+ CHECK(context_);
+
+ if (!MinidumpWritable::Freeze()) {
+ return false;
+ }
+
+ if (stack_) {
+ stack_->RegisterMemoryDescriptor(&thread_.Stack);
+ }
+
+ context_->RegisterLocationDescriptor(&thread_.ThreadContext);
+
+ return true;
+}
+
+size_t MinidumpThreadWriter::SizeOfObject() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ // This object doesn’t directly write anything itself. Its MINIDUMP_THREAD is
+ // written by its parent as part of a MINIDUMP_THREAD_LIST, and its children
+ // are responsible for writing themselves.
+ return 0;
+}
+
+std::vector<internal::MinidumpWritable*> MinidumpThreadWriter::Children() {
+ DCHECK_GE(state(), kStateFrozen);
+ DCHECK(context_);
+
+ std::vector<MinidumpWritable*> children;
+ if (stack_) {
+ children.push_back(stack_.get());
+ }
+ children.push_back(context_.get());
+
+ return children;
+}
+
+bool MinidumpThreadWriter::WriteObject(FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateWritable);
+
+ // This object doesn’t directly write anything itself. Its MINIDUMP_THREAD is
+ // written by its parent as part of a MINIDUMP_THREAD_LIST, and its children
+ // are responsible for writing themselves.
+ return true;
+}
+
+MinidumpThreadListWriter::MinidumpThreadListWriter()
+ : MinidumpStreamWriter(),
+ threads_(),
+ memory_list_writer_(nullptr),
+ thread_list_base_() {
+}
+
+MinidumpThreadListWriter::~MinidumpThreadListWriter() {
+}
+
+void MinidumpThreadListWriter::InitializeFromSnapshot(
+ const std::vector<const ThreadSnapshot*>& thread_snapshots,
+ MinidumpThreadIDMap* thread_id_map) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK(threads_.empty());
+
+ BuildMinidumpThreadIDMap(thread_snapshots, thread_id_map);
+
+ for (const ThreadSnapshot* thread_snapshot : thread_snapshots) {
+ auto thread = make_scoped_ptr(new MinidumpThreadWriter());
+ thread->InitializeFromSnapshot(thread_snapshot, thread_id_map);
+ AddThread(thread.Pass());
+ }
+}
+
+void MinidumpThreadListWriter::SetMemoryListWriter(
+ MinidumpMemoryListWriter* memory_list_writer) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK(threads_.empty());
+
+ memory_list_writer_ = memory_list_writer;
+}
+
+void MinidumpThreadListWriter::AddThread(
+ scoped_ptr<MinidumpThreadWriter> thread) {
+ DCHECK_EQ(state(), kStateMutable);
+
+ if (memory_list_writer_) {
+ MinidumpMemoryWriter* stack = thread->Stack();
+ if (stack) {
+ memory_list_writer_->AddExtraMemory(stack);
+ }
+ }
+
+ threads_.push_back(thread.release());
+}
+
+bool MinidumpThreadListWriter::Freeze() {
+ DCHECK_EQ(state(), kStateMutable);
+
+ if (!MinidumpStreamWriter::Freeze()) {
+ return false;
+ }
+
+ size_t thread_count = threads_.size();
+ if (!AssignIfInRange(&thread_list_base_.NumberOfThreads, thread_count)) {
+ LOG(ERROR) << "thread_count " << thread_count << " out of range";
+ return false;
+ }
+
+ return true;
+}
+
+size_t MinidumpThreadListWriter::SizeOfObject() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ return sizeof(thread_list_base_) + threads_.size() * sizeof(MINIDUMP_THREAD);
+}
+
+std::vector<internal::MinidumpWritable*> MinidumpThreadListWriter::Children() {
+ DCHECK_GE(state(), kStateFrozen);
+
+ std::vector<MinidumpWritable*> children;
+ for (MinidumpThreadWriter* thread : threads_) {
+ children.push_back(thread);
+ }
+
+ return children;
+}
+
+bool MinidumpThreadListWriter::WriteObject(FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateWritable);
+
+ WritableIoVec iov;
+ iov.iov_base = &thread_list_base_;
+ iov.iov_len = sizeof(thread_list_base_);
+ std::vector<WritableIoVec> iovecs(1, iov);
+
+ for (const MinidumpThreadWriter* thread : threads_) {
+ iov.iov_base = thread->MinidumpThread();
+ iov.iov_len = sizeof(MINIDUMP_THREAD);
+ iovecs.push_back(iov);
+ }
+
+ return file_writer->WriteIoVec(&iovecs);
+}
+
+MinidumpStreamType MinidumpThreadListWriter::StreamType() const {
+ return kMinidumpStreamTypeThreadList;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer.h
new file mode 100644
index 00000000000..f8b7d20106d
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer.h
@@ -0,0 +1,214 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_MINIDUMP_MINIDUMP_THREAD_WRITER_H_
+#define CRASHPAD_MINIDUMP_MINIDUMP_THREAD_WRITER_H_
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "minidump/minidump_stream_writer.h"
+#include "minidump/minidump_thread_id_map.h"
+#include "minidump/minidump_writable.h"
+#include "util/stdlib/pointer_container.h"
+
+namespace crashpad {
+
+class MinidumpContextWriter;
+class MinidumpMemoryListWriter;
+class MinidumpMemoryWriter;
+class ThreadSnapshot;
+
+//! \brief The writer for a MINIDUMP_THREAD object in a minidump file.
+//!
+//! Because MINIDUMP_THREAD objects only appear as elements of
+//! MINIDUMP_THREAD_LIST objects, this class does not write any data on its own.
+//! It makes its MINIDUMP_THREAD data available to its MinidumpThreadListWriter
+//! parent, which writes it as part of a MINIDUMP_THREAD_LIST.
+class MinidumpThreadWriter final : public internal::MinidumpWritable {
+ public:
+ MinidumpThreadWriter();
+ ~MinidumpThreadWriter() override;
+
+ //! \brief Initializes the MINIDUMP_THREAD based on \a thread_snapshot.
+ //!
+ //! \param[in] thread_snapshot The thread snapshot to use as source data.
+ //! \param[in] thread_id_map A MinidumpThreadIDMap to be consulted to
+ //! determine the 32-bit minidump thread ID to use for \a thread_snapshot.
+ //!
+ //! \note Valid in #kStateMutable. No mutator methods may be called before
+ //! this method, and it is not normally necessary to call any mutator
+ //! methods after this method.
+ void InitializeFromSnapshot(const ThreadSnapshot* thread_snapshot,
+ const MinidumpThreadIDMap* thread_id_map);
+
+ //! \brief Returns a MINIDUMP_THREAD referencing this object’s data.
+ //!
+ //! This method is expected to be called by a MinidumpThreadListWriter in
+ //! order to obtain a MINIDUMP_THREAD to include in its list.
+ //!
+ //! \note Valid in #kStateWritable.
+ const MINIDUMP_THREAD* MinidumpThread() const;
+
+ //! \brief Returns a MinidumpMemoryWriter that will write the memory region
+ //! corresponding to this object’s stack.
+ //!
+ //! If the thread does not have a stack, or its stack could not be determined,
+ //! this will return `nullptr`.
+ //!
+ //! This method is provided so that MinidumpThreadListWriter can obtain thread
+ //! stack memory regions for the purposes of adding them to a
+ //! MinidumpMemoryListWriter (configured by calling
+ //! MinidumpThreadListWriter::SetMemoryListWriter()) by calling
+ //! MinidumpMemoryListWriter::AddExtraMemory().
+ //!
+ //! \note Valid in any state.
+ MinidumpMemoryWriter* Stack() const { return stack_.get(); }
+
+ //! \brief Arranges for MINIDUMP_THREAD::Stack to point to the MINIDUMP_MEMORY
+ //! object to be written by \a stack.
+ //!
+ //! This object takes ownership of \a stack and becomes its parent in the
+ //! overall tree of internal::MinidumpWritable objects.
+ //!
+ //! \note Valid in #kStateMutable.
+ void SetStack(scoped_ptr<MinidumpMemoryWriter> stack);
+
+ //! \brief Arranges for MINIDUMP_THREAD::ThreadContext to point to the CPU
+ //! context to be written by \a context.
+ //!
+ //! A context is required in all MINIDUMP_THREAD objects.
+ //!
+ //! This object takes ownership of \a context and becomes its parent in the
+ //! overall tree of internal::MinidumpWritable objects.
+ //!
+ //! \note Valid in #kStateMutable.
+ void SetContext(scoped_ptr<MinidumpContextWriter> context);
+
+ //! \brief Sets MINIDUMP_THREAD::ThreadId.
+ void SetThreadID(uint32_t thread_id) { thread_.ThreadId = thread_id; }
+
+ //! \brief Sets MINIDUMP_THREAD::SuspendCount.
+ void SetSuspendCount(uint32_t suspend_count) {
+ thread_.SuspendCount = suspend_count;
+ }
+
+ //! \brief Sets MINIDUMP_THREAD::PriorityClass.
+ void SetPriorityClass(uint32_t priority_class) {
+ thread_.PriorityClass = priority_class;
+ }
+
+ //! \brief Sets MINIDUMP_THREAD::Priority.
+ void SetPriority(uint32_t priority) { thread_.Priority = priority; }
+
+ //! \brief Sets MINIDUMP_THREAD::Teb.
+ void SetTEB(uint64_t teb) { thread_.Teb = teb; }
+
+ protected:
+ // MinidumpWritable:
+ bool Freeze() override;
+ size_t SizeOfObject() override;
+ std::vector<MinidumpWritable*> Children() override;
+ bool WriteObject(FileWriterInterface* file_writer) override;
+
+ private:
+ MINIDUMP_THREAD thread_;
+ scoped_ptr<MinidumpMemoryWriter> stack_;
+ scoped_ptr<MinidumpContextWriter> context_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpThreadWriter);
+};
+
+//! \brief The writer for a MINIDUMP_THREAD_LIST stream in a minidump file,
+//! containing a list of MINIDUMP_THREAD objects.
+class MinidumpThreadListWriter final : public internal::MinidumpStreamWriter {
+ public:
+ MinidumpThreadListWriter();
+ ~MinidumpThreadListWriter() override;
+
+ //! \brief Adds an initialized MINIDUMP_THREAD for each thread in \a
+ //! thread_snapshots to the MINIDUMP_THREAD_LIST.
+ //!
+ //! \param[in] thread_snapshots The thread snapshots to use as source data.
+ //! \param[out] thread_id_map A MinidumpThreadIDMap to be built by this
+ //! method. This map must be empty when this method is called.
+ //!
+ //! \note Valid in #kStateMutable. AddThread() may not be called before this
+ //! method, and it is not normally necessary to call AddThread() after
+ //! this method.
+ void InitializeFromSnapshot(
+ const std::vector<const ThreadSnapshot*>& thread_snapshots,
+ MinidumpThreadIDMap* thread_id_map);
+
+ //! \brief Sets the MinidumpMemoryListWriter that each thread’s stack memory
+ //! region should be added to as extra memory.
+ //!
+ //! Each MINIDUMP_THREAD object can contain a reference to a
+ //! MinidumpMemoryWriter object that contains a snapshot of its stack memory.
+ //! In the overall tree of internal::MinidumpWritable objects, these
+ //! MinidumpMemoryWriter objects are considered children of their
+ //! MINIDUMP_THREAD, and are referenced by a MINIDUMP_MEMORY_DESCRIPTOR
+ //! contained in the MINIDUMP_THREAD. It is also possible for the same memory
+ //! regions to have MINIDUMP_MEMORY_DESCRIPTOR objects present in a
+ //! MINIDUMP_MEMORY_LIST stream. This is accomplished by calling this method,
+ //! which informs a MinidumpThreadListWriter that it should call
+ //! MinidumpMemoryListWriter::AddExtraMemory() for each extant thread stack
+ //! while the thread is being added in AddThread(). When this is done, the
+ //! MinidumpMemoryListWriter will contain a MINIDUMP_MEMORY_DESCRIPTOR
+ //! pointing to the thread’s stack memory in its MINIDUMP_MEMORY_LIST. Note
+ //! that the actual contents of the memory is only written once, as a child of
+ //! the MinidumpThreadWriter. The MINIDUMP_MEMORY_DESCRIPTOR objects in both
+ //! the MINIDUMP_THREAD and MINIDUMP_MEMORY_LIST will point to the same copy
+ //! of the memory’s contents.
+ //!
+ //! \note This method must be called before AddThread() is called. Threads
+ //! added by AddThread() prior to this method being called will not have
+ //! their stacks added to \a memory_list_writer as extra memory.
+ //! \note Valid in #kStateMutable.
+ void SetMemoryListWriter(MinidumpMemoryListWriter* memory_list_writer);
+
+ //! \brief Adds a MinidumpThreadWriter to the MINIDUMP_THREAD_LIST.
+ //!
+ //! This object takes ownership of \a thread and becomes its parent in the
+ //! overall tree of internal::MinidumpWritable objects.
+ //!
+ //! \note Valid in #kStateMutable.
+ void AddThread(scoped_ptr<MinidumpThreadWriter> thread);
+
+ protected:
+ // MinidumpWritable:
+ bool Freeze() override;
+ size_t SizeOfObject() override;
+ std::vector<MinidumpWritable*> Children() override;
+ bool WriteObject(FileWriterInterface* file_writer) override;
+
+ // MinidumpStreamWriter:
+ MinidumpStreamType StreamType() const override;
+
+ private:
+ PointerVector<MinidumpThreadWriter> threads_;
+ MinidumpMemoryListWriter* memory_list_writer_; // weak
+ MINIDUMP_THREAD_LIST thread_list_base_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpThreadListWriter);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_MINIDUMP_MINIDUMP_THREAD_WRITER_H_
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer_test.cc
new file mode 100644
index 00000000000..76087a377c8
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer_test.cc
@@ -0,0 +1,690 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_thread_writer.h"
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <sys/types.h>
+
+#include "base/compiler_specific.h"
+#include "base/format_macros.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+#include "minidump/minidump_context_writer.h"
+#include "minidump/minidump_memory_writer.h"
+#include "minidump/minidump_file_writer.h"
+#include "minidump/minidump_thread_id_map.h"
+#include "minidump/test/minidump_context_test_util.h"
+#include "minidump/test/minidump_memory_writer_test_util.h"
+#include "minidump/test/minidump_file_writer_test_util.h"
+#include "minidump/test/minidump_writable_test_util.h"
+#include "snapshot/test/test_cpu_context.h"
+#include "snapshot/test/test_memory_snapshot.h"
+#include "snapshot/test/test_thread_snapshot.h"
+#include "test/gtest_death_check.h"
+#include "util/file/string_file.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+// This returns the MINIDUMP_THREAD_LIST stream in |thread_list|. If
+// |memory_list| is not nullptr, a MINIDUMP_MEMORY_LIST stream is also expected
+// in |file_contents|, and that stream will be returned in |memory_list|.
+void GetThreadListStream(const std::string& file_contents,
+ const MINIDUMP_THREAD_LIST** thread_list,
+ const MINIDUMP_MEMORY_LIST** memory_list) {
+ const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
+ const uint32_t kExpectedStreams = memory_list ? 2 : 1;
+ const size_t kThreadListStreamOffset =
+ kDirectoryOffset + kExpectedStreams * sizeof(MINIDUMP_DIRECTORY);
+ const size_t kThreadsOffset =
+ kThreadListStreamOffset + sizeof(MINIDUMP_THREAD_LIST);
+
+ ASSERT_GE(file_contents.size(), kThreadsOffset);
+
+ const MINIDUMP_DIRECTORY* directory;
+ const MINIDUMP_HEADER* header =
+ MinidumpHeaderAtStart(file_contents, &directory);
+ ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, kExpectedStreams, 0));
+ ASSERT_TRUE(directory);
+
+ ASSERT_EQ(kMinidumpStreamTypeThreadList, directory[0].StreamType);
+ EXPECT_EQ(kThreadListStreamOffset, directory[0].Location.Rva);
+
+ *thread_list = MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_LIST>(
+ file_contents, directory[0].Location);
+ ASSERT_TRUE(thread_list);
+
+ if (memory_list) {
+ ASSERT_EQ(kMinidumpStreamTypeMemoryList, directory[1].StreamType);
+
+ *memory_list = MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>(
+ file_contents, directory[1].Location);
+ ASSERT_TRUE(*memory_list);
+ }
+}
+
+TEST(MinidumpThreadWriter, EmptyThreadList) {
+ MinidumpFileWriter minidump_file_writer;
+ auto thread_list_writer = make_scoped_ptr(new MinidumpThreadListWriter());
+
+ minidump_file_writer.AddStream(thread_list_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
+ sizeof(MINIDUMP_THREAD_LIST),
+ string_file.string().size());
+
+ const MINIDUMP_THREAD_LIST* thread_list = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ GetThreadListStream(string_file.string(), &thread_list, nullptr));
+
+ EXPECT_EQ(0u, thread_list->NumberOfThreads);
+}
+
+// The MINIDUMP_THREADs |expected| and |observed| are compared against each
+// other using gtest assertions. If |stack| is not nullptr, |observed| is
+// expected to contain a populated MINIDUMP_MEMORY_DESCRIPTOR in its Stack
+// field, otherwise, its Stack field is expected to be zeroed out. The memory
+// descriptor will be placed in |stack|. |observed| must contain a populated
+// ThreadContext field. The context will be recovered from |file_contents| and
+// stored in |context_base|.
+void ExpectThread(const MINIDUMP_THREAD* expected,
+ const MINIDUMP_THREAD* observed,
+ const std::string& file_contents,
+ const MINIDUMP_MEMORY_DESCRIPTOR** stack,
+ const void** context_base) {
+ EXPECT_EQ(expected->ThreadId, observed->ThreadId);
+ EXPECT_EQ(expected->SuspendCount, observed->SuspendCount);
+ EXPECT_EQ(expected->PriorityClass, observed->PriorityClass);
+ EXPECT_EQ(expected->Priority, observed->Priority);
+ EXPECT_EQ(expected->Teb, observed->Teb);
+
+ EXPECT_EQ(expected->Stack.StartOfMemoryRange,
+ observed->Stack.StartOfMemoryRange);
+ EXPECT_EQ(expected->Stack.Memory.DataSize, observed->Stack.Memory.DataSize);
+ if (stack) {
+ ASSERT_NE(0u, observed->Stack.Memory.DataSize);
+ ASSERT_NE(0u, observed->Stack.Memory.Rva);
+ ASSERT_GE(file_contents.size(),
+ observed->Stack.Memory.Rva + observed->Stack.Memory.DataSize);
+ *stack = &observed->Stack;
+ } else {
+ EXPECT_EQ(0u, observed->Stack.StartOfMemoryRange);
+ EXPECT_EQ(0u, observed->Stack.Memory.DataSize);
+ EXPECT_EQ(0u, observed->Stack.Memory.Rva);
+ }
+
+ EXPECT_EQ(expected->ThreadContext.DataSize, observed->ThreadContext.DataSize);
+ ASSERT_NE(0u, observed->ThreadContext.DataSize);
+ ASSERT_NE(0u, observed->ThreadContext.Rva);
+ ASSERT_GE(file_contents.size(),
+ observed->ThreadContext.Rva + expected->ThreadContext.DataSize);
+ *context_base = &file_contents[observed->ThreadContext.Rva];
+}
+
+TEST(MinidumpThreadWriter, OneThread_x86_NoStack) {
+ MinidumpFileWriter minidump_file_writer;
+ auto thread_list_writer = make_scoped_ptr(new MinidumpThreadListWriter());
+
+ const uint32_t kThreadID = 0x11111111;
+ const uint32_t kSuspendCount = 1;
+ const uint32_t kPriorityClass = 0x20;
+ const uint32_t kPriority = 10;
+ const uint64_t kTEB = 0x55555555;
+ const uint32_t kSeed = 123;
+
+ auto thread_writer = make_scoped_ptr(new MinidumpThreadWriter());
+ thread_writer->SetThreadID(kThreadID);
+ thread_writer->SetSuspendCount(kSuspendCount);
+ thread_writer->SetPriorityClass(kPriorityClass);
+ thread_writer->SetPriority(kPriority);
+ thread_writer->SetTEB(kTEB);
+
+ auto context_x86_writer = make_scoped_ptr(new MinidumpContextX86Writer());
+ InitializeMinidumpContextX86(context_x86_writer->context(), kSeed);
+ thread_writer->SetContext(context_x86_writer.Pass());
+
+ thread_list_writer->AddThread(thread_writer.Pass());
+ minidump_file_writer.AddStream(thread_list_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
+ sizeof(MINIDUMP_THREAD_LIST) + 1 * sizeof(MINIDUMP_THREAD) +
+ 1 * sizeof(MinidumpContextX86),
+ string_file.string().size());
+
+ const MINIDUMP_THREAD_LIST* thread_list = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ GetThreadListStream(string_file.string(), &thread_list, nullptr));
+
+ EXPECT_EQ(1u, thread_list->NumberOfThreads);
+
+ MINIDUMP_THREAD expected = {};
+ expected.ThreadId = kThreadID;
+ expected.SuspendCount = kSuspendCount;
+ expected.PriorityClass = kPriorityClass;
+ expected.Priority = kPriority;
+ expected.Teb = kTEB;
+ expected.ThreadContext.DataSize = sizeof(MinidumpContextX86);
+
+ const MinidumpContextX86* observed_context = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ ExpectThread(&expected,
+ &thread_list->Threads[0],
+ string_file.string(),
+ nullptr,
+ reinterpret_cast<const void**>(&observed_context)));
+
+ ASSERT_NO_FATAL_FAILURE(
+ ExpectMinidumpContextX86(kSeed, observed_context, false));
+}
+
+TEST(MinidumpThreadWriter, OneThread_AMD64_Stack) {
+ MinidumpFileWriter minidump_file_writer;
+ auto thread_list_writer = make_scoped_ptr(new MinidumpThreadListWriter());
+
+ const uint32_t kThreadID = 0x22222222;
+ const uint32_t kSuspendCount = 2;
+ const uint32_t kPriorityClass = 0x30;
+ const uint32_t kPriority = 20;
+ const uint64_t kTEB = 0x5555555555555555;
+ const uint64_t kMemoryBase = 0x765432100000;
+ const size_t kMemorySize = 32;
+ const uint8_t kMemoryValue = 99;
+ const uint32_t kSeed = 456;
+
+ auto thread_writer = make_scoped_ptr(new MinidumpThreadWriter());
+ thread_writer->SetThreadID(kThreadID);
+ thread_writer->SetSuspendCount(kSuspendCount);
+ thread_writer->SetPriorityClass(kPriorityClass);
+ thread_writer->SetPriority(kPriority);
+ thread_writer->SetTEB(kTEB);
+
+ auto memory_writer = make_scoped_ptr(
+ new TestMinidumpMemoryWriter(kMemoryBase, kMemorySize, kMemoryValue));
+ thread_writer->SetStack(memory_writer.Pass());
+
+ MSVC_SUPPRESS_WARNING(4316); // Object allocated on heap may not be aligned.
+ auto context_amd64_writer = make_scoped_ptr(new MinidumpContextAMD64Writer());
+ InitializeMinidumpContextAMD64(context_amd64_writer->context(), kSeed);
+ thread_writer->SetContext(context_amd64_writer.Pass());
+
+ thread_list_writer->AddThread(thread_writer.Pass());
+ minidump_file_writer.AddStream(thread_list_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
+ sizeof(MINIDUMP_THREAD_LIST) + 1 * sizeof(MINIDUMP_THREAD) +
+ 1 * sizeof(MinidumpContextAMD64) + kMemorySize,
+ string_file.string().size());
+
+ const MINIDUMP_THREAD_LIST* thread_list = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ GetThreadListStream(string_file.string(), &thread_list, nullptr));
+
+ EXPECT_EQ(1u, thread_list->NumberOfThreads);
+
+ MINIDUMP_THREAD expected = {};
+ expected.ThreadId = kThreadID;
+ expected.SuspendCount = kSuspendCount;
+ expected.PriorityClass = kPriorityClass;
+ expected.Priority = kPriority;
+ expected.Teb = kTEB;
+ expected.Stack.StartOfMemoryRange = kMemoryBase;
+ expected.Stack.Memory.DataSize = kMemorySize;
+ expected.ThreadContext.DataSize = sizeof(MinidumpContextAMD64);
+
+ const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack = nullptr;
+ const MinidumpContextAMD64* observed_context = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ ExpectThread(&expected,
+ &thread_list->Threads[0],
+ string_file.string(),
+ &observed_stack,
+ reinterpret_cast<const void**>(&observed_context)));
+
+ ASSERT_NO_FATAL_FAILURE(
+ ExpectMinidumpMemoryDescriptorAndContents(&expected.Stack,
+ observed_stack,
+ string_file.string(),
+ kMemoryValue,
+ true));
+ ASSERT_NO_FATAL_FAILURE(
+ ExpectMinidumpContextAMD64(kSeed, observed_context, false));
+}
+
+TEST(MinidumpThreadWriter, ThreeThreads_x86_MemoryList) {
+ MinidumpFileWriter minidump_file_writer;
+ auto thread_list_writer = make_scoped_ptr(new MinidumpThreadListWriter());
+ auto memory_list_writer = make_scoped_ptr(new MinidumpMemoryListWriter());
+ thread_list_writer->SetMemoryListWriter(memory_list_writer.get());
+
+ const uint32_t kThreadID0 = 1111111;
+ const uint32_t kSuspendCount0 = 111111;
+ const uint32_t kPriorityClass0 = 11111;
+ const uint32_t kPriority0 = 1111;
+ const uint64_t kTEB0 = 111;
+ const uint64_t kMemoryBase0 = 0x1110;
+ const size_t kMemorySize0 = 16;
+ const uint8_t kMemoryValue0 = 11;
+ const uint32_t kSeed0 = 1;
+
+ auto thread_writer_0 = make_scoped_ptr(new MinidumpThreadWriter());
+ thread_writer_0->SetThreadID(kThreadID0);
+ thread_writer_0->SetSuspendCount(kSuspendCount0);
+ thread_writer_0->SetPriorityClass(kPriorityClass0);
+ thread_writer_0->SetPriority(kPriority0);
+ thread_writer_0->SetTEB(kTEB0);
+
+ auto memory_writer_0 = make_scoped_ptr(
+ new TestMinidumpMemoryWriter(kMemoryBase0, kMemorySize0, kMemoryValue0));
+ thread_writer_0->SetStack(memory_writer_0.Pass());
+
+ auto context_x86_writer_0 = make_scoped_ptr(new MinidumpContextX86Writer());
+ InitializeMinidumpContextX86(context_x86_writer_0->context(), kSeed0);
+ thread_writer_0->SetContext(context_x86_writer_0.Pass());
+
+ thread_list_writer->AddThread(thread_writer_0.Pass());
+
+ const uint32_t kThreadID1 = 2222222;
+ const uint32_t kSuspendCount1 = 222222;
+ const uint32_t kPriorityClass1 = 22222;
+ const uint32_t kPriority1 = 2222;
+ const uint64_t kTEB1 = 222;
+ const uint64_t kMemoryBase1 = 0x2220;
+ const size_t kMemorySize1 = 32;
+ const uint8_t kMemoryValue1 = 22;
+ const uint32_t kSeed1 = 2;
+
+ auto thread_writer_1 = make_scoped_ptr(new MinidumpThreadWriter());
+ thread_writer_1->SetThreadID(kThreadID1);
+ thread_writer_1->SetSuspendCount(kSuspendCount1);
+ thread_writer_1->SetPriorityClass(kPriorityClass1);
+ thread_writer_1->SetPriority(kPriority1);
+ thread_writer_1->SetTEB(kTEB1);
+
+ auto memory_writer_1 = make_scoped_ptr(
+ new TestMinidumpMemoryWriter(kMemoryBase1, kMemorySize1, kMemoryValue1));
+ thread_writer_1->SetStack(memory_writer_1.Pass());
+
+ auto context_x86_writer_1 = make_scoped_ptr(new MinidumpContextX86Writer());
+ InitializeMinidumpContextX86(context_x86_writer_1->context(), kSeed1);
+ thread_writer_1->SetContext(context_x86_writer_1.Pass());
+
+ thread_list_writer->AddThread(thread_writer_1.Pass());
+
+ const uint32_t kThreadID2 = 3333333;
+ const uint32_t kSuspendCount2 = 333333;
+ const uint32_t kPriorityClass2 = 33333;
+ const uint32_t kPriority2 = 3333;
+ const uint64_t kTEB2 = 333;
+ const uint64_t kMemoryBase2 = 0x3330;
+ const size_t kMemorySize2 = 48;
+ const uint8_t kMemoryValue2 = 33;
+ const uint32_t kSeed2 = 3;
+
+ auto thread_writer_2 = make_scoped_ptr(new MinidumpThreadWriter());
+ thread_writer_2->SetThreadID(kThreadID2);
+ thread_writer_2->SetSuspendCount(kSuspendCount2);
+ thread_writer_2->SetPriorityClass(kPriorityClass2);
+ thread_writer_2->SetPriority(kPriority2);
+ thread_writer_2->SetTEB(kTEB2);
+
+ auto memory_writer_2 = make_scoped_ptr(
+ new TestMinidumpMemoryWriter(kMemoryBase2, kMemorySize2, kMemoryValue2));
+ thread_writer_2->SetStack(memory_writer_2.Pass());
+
+ auto context_x86_writer_2 = make_scoped_ptr(new MinidumpContextX86Writer());
+ InitializeMinidumpContextX86(context_x86_writer_2->context(), kSeed2);
+ thread_writer_2->SetContext(context_x86_writer_2.Pass());
+
+ thread_list_writer->AddThread(thread_writer_2.Pass());
+
+ minidump_file_writer.AddStream(thread_list_writer.Pass());
+ minidump_file_writer.AddStream(memory_list_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ ASSERT_EQ(sizeof(MINIDUMP_HEADER) + 2 * sizeof(MINIDUMP_DIRECTORY) +
+ sizeof(MINIDUMP_THREAD_LIST) + 3 * sizeof(MINIDUMP_THREAD) +
+ sizeof(MINIDUMP_MEMORY_LIST) +
+ 3 * sizeof(MINIDUMP_MEMORY_DESCRIPTOR) +
+ 3 * sizeof(MinidumpContextX86) + kMemorySize0 + kMemorySize1 +
+ kMemorySize2 + 12, // 12 for alignment
+ string_file.string().size());
+
+ const MINIDUMP_THREAD_LIST* thread_list = nullptr;
+ const MINIDUMP_MEMORY_LIST* memory_list = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ GetThreadListStream(string_file.string(), &thread_list, &memory_list));
+
+ EXPECT_EQ(3u, thread_list->NumberOfThreads);
+ EXPECT_EQ(3u, memory_list->NumberOfMemoryRanges);
+
+ {
+ SCOPED_TRACE("thread 0");
+
+ MINIDUMP_THREAD expected = {};
+ expected.ThreadId = kThreadID0;
+ expected.SuspendCount = kSuspendCount0;
+ expected.PriorityClass = kPriorityClass0;
+ expected.Priority = kPriority0;
+ expected.Teb = kTEB0;
+ expected.Stack.StartOfMemoryRange = kMemoryBase0;
+ expected.Stack.Memory.DataSize = kMemorySize0;
+ expected.ThreadContext.DataSize = sizeof(MinidumpContextX86);
+
+ const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack = nullptr;
+ const MinidumpContextX86* observed_context = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ ExpectThread(&expected,
+ &thread_list->Threads[0],
+ string_file.string(),
+ &observed_stack,
+ reinterpret_cast<const void**>(&observed_context)));
+
+ ASSERT_NO_FATAL_FAILURE(
+ ExpectMinidumpMemoryDescriptorAndContents(&expected.Stack,
+ observed_stack,
+ string_file.string(),
+ kMemoryValue0,
+ false));
+ ASSERT_NO_FATAL_FAILURE(
+ ExpectMinidumpContextX86(kSeed0, observed_context, false));
+ ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor(
+ observed_stack, &memory_list->MemoryRanges[0]));
+ }
+
+ {
+ SCOPED_TRACE("thread 1");
+
+ MINIDUMP_THREAD expected = {};
+ expected.ThreadId = kThreadID1;
+ expected.SuspendCount = kSuspendCount1;
+ expected.PriorityClass = kPriorityClass1;
+ expected.Priority = kPriority1;
+ expected.Teb = kTEB1;
+ expected.Stack.StartOfMemoryRange = kMemoryBase1;
+ expected.Stack.Memory.DataSize = kMemorySize1;
+ expected.ThreadContext.DataSize = sizeof(MinidumpContextX86);
+
+ const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack = nullptr;
+ const MinidumpContextX86* observed_context = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ ExpectThread(&expected,
+ &thread_list->Threads[1],
+ string_file.string(),
+ &observed_stack,
+ reinterpret_cast<const void**>(&observed_context)));
+
+ ASSERT_NO_FATAL_FAILURE(
+ ExpectMinidumpMemoryDescriptorAndContents(&expected.Stack,
+ observed_stack,
+ string_file.string(),
+ kMemoryValue1,
+ false));
+ ASSERT_NO_FATAL_FAILURE(
+ ExpectMinidumpContextX86(kSeed1, observed_context, false));
+ ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor(
+ observed_stack, &memory_list->MemoryRanges[1]));
+ }
+
+ {
+ SCOPED_TRACE("thread 2");
+
+ MINIDUMP_THREAD expected = {};
+ expected.ThreadId = kThreadID2;
+ expected.SuspendCount = kSuspendCount2;
+ expected.PriorityClass = kPriorityClass2;
+ expected.Priority = kPriority2;
+ expected.Teb = kTEB2;
+ expected.Stack.StartOfMemoryRange = kMemoryBase2;
+ expected.Stack.Memory.DataSize = kMemorySize2;
+ expected.ThreadContext.DataSize = sizeof(MinidumpContextX86);
+
+ const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack = nullptr;
+ const MinidumpContextX86* observed_context = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ ExpectThread(&expected,
+ &thread_list->Threads[2],
+ string_file.string(),
+ &observed_stack,
+ reinterpret_cast<const void**>(&observed_context)));
+
+ ASSERT_NO_FATAL_FAILURE(
+ ExpectMinidumpMemoryDescriptorAndContents(&expected.Stack,
+ observed_stack,
+ string_file.string(),
+ kMemoryValue2,
+ true));
+ ASSERT_NO_FATAL_FAILURE(
+ ExpectMinidumpContextX86(kSeed2, observed_context, false));
+ ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor(
+ observed_stack, &memory_list->MemoryRanges[2]));
+ }
+}
+
+struct InitializeFromSnapshotX86Traits {
+ using MinidumpContextType = MinidumpContextX86;
+ static void InitializeCPUContext(CPUContext* context, uint32_t seed) {
+ return InitializeCPUContextX86(context, seed);
+ }
+ static void ExpectMinidumpContext(
+ uint32_t expect_seed, const MinidumpContextX86* observed, bool snapshot) {
+ return ExpectMinidumpContextX86(expect_seed, observed, snapshot);
+ }
+};
+
+struct InitializeFromSnapshotAMD64Traits {
+ using MinidumpContextType = MinidumpContextAMD64;
+ static void InitializeCPUContext(CPUContext* context, uint32_t seed) {
+ return InitializeCPUContextX86_64(context, seed);
+ }
+ static void ExpectMinidumpContext(uint32_t expect_seed,
+ const MinidumpContextAMD64* observed,
+ bool snapshot) {
+ return ExpectMinidumpContextAMD64(expect_seed, observed, snapshot);
+ }
+};
+
+struct InitializeFromSnapshotNoContextTraits {
+ using MinidumpContextType = MinidumpContextX86;
+ static void InitializeCPUContext(CPUContext* context, uint32_t seed) {
+ context->architecture = kCPUArchitectureUnknown;
+ }
+ static void ExpectMinidumpContext(uint32_t expect_seed,
+ const MinidumpContextX86* observed,
+ bool snapshot) {
+ FAIL();
+ }
+};
+
+template <typename Traits>
+void RunInitializeFromSnapshotTest(bool thread_id_collision) {
+ using MinidumpContextType = typename Traits::MinidumpContextType;
+ MINIDUMP_THREAD expect_threads[3] = {};
+ uint64_t thread_ids[arraysize(expect_threads)] = {};
+ uint8_t memory_values[arraysize(expect_threads)] = {};
+ uint32_t context_seeds[arraysize(expect_threads)] = {};
+
+ expect_threads[0].ThreadId = 1;
+ expect_threads[0].SuspendCount = 2;
+ expect_threads[0].Priority = 3;
+ expect_threads[0].Teb = 0x0123456789abcdef;
+ expect_threads[0].Stack.StartOfMemoryRange = 0x1000;
+ expect_threads[0].Stack.Memory.DataSize = 0x100;
+ expect_threads[0].ThreadContext.DataSize = sizeof(MinidumpContextType);
+ memory_values[0] = 'A';
+ context_seeds[0] = 0x80000000;
+
+ // The thread at index 1 has no stack.
+ expect_threads[1].ThreadId = 11;
+ expect_threads[1].SuspendCount = 12;
+ expect_threads[1].Priority = 13;
+ expect_threads[1].Teb = 0xfedcba9876543210;
+ expect_threads[1].ThreadContext.DataSize = sizeof(MinidumpContextType);
+ context_seeds[1] = 0x40000001;
+
+ expect_threads[2].ThreadId = 21;
+ expect_threads[2].SuspendCount = 22;
+ expect_threads[2].Priority = 23;
+ expect_threads[2].Teb = 0x1111111111111111;
+ expect_threads[2].Stack.StartOfMemoryRange = 0x3000;
+ expect_threads[2].Stack.Memory.DataSize = 0x300;
+ expect_threads[2].ThreadContext.DataSize = sizeof(MinidumpContextType);
+ memory_values[2] = 'd';
+ context_seeds[2] = 0x20000002;
+
+ if (thread_id_collision) {
+ thread_ids[0] = 0x0123456700000001;
+ thread_ids[1] = 0x89abcdef00000001;
+ thread_ids[2] = 4;
+ expect_threads[0].ThreadId = 0;
+ expect_threads[1].ThreadId = 1;
+ expect_threads[2].ThreadId = 2;
+ } else {
+ thread_ids[0] = 1;
+ thread_ids[1] = 11;
+ thread_ids[2] = 22;
+ expect_threads[0].ThreadId = static_cast<uint32_t>(thread_ids[0]);
+ expect_threads[1].ThreadId = static_cast<uint32_t>(thread_ids[1]);
+ expect_threads[2].ThreadId = static_cast<uint32_t>(thread_ids[2]);
+ }
+
+ PointerVector<TestThreadSnapshot> thread_snapshots_owner;
+ std::vector<const ThreadSnapshot*> thread_snapshots;
+ for (size_t index = 0; index < arraysize(expect_threads); ++index) {
+ TestThreadSnapshot* thread_snapshot = new TestThreadSnapshot();
+ thread_snapshots_owner.push_back(thread_snapshot);
+
+ thread_snapshot->SetThreadID(thread_ids[index]);
+ thread_snapshot->SetSuspendCount(expect_threads[index].SuspendCount);
+ thread_snapshot->SetPriority(expect_threads[index].Priority);
+ thread_snapshot->SetThreadSpecificDataAddress(expect_threads[index].Teb);
+
+ if (expect_threads[index].Stack.Memory.DataSize) {
+ auto memory_snapshot = make_scoped_ptr(new TestMemorySnapshot());
+ memory_snapshot->SetAddress(
+ expect_threads[index].Stack.StartOfMemoryRange);
+ memory_snapshot->SetSize(expect_threads[index].Stack.Memory.DataSize);
+ memory_snapshot->SetValue(memory_values[index]);
+ thread_snapshot->SetStack(memory_snapshot.Pass());
+ }
+
+ Traits::InitializeCPUContext(thread_snapshot->MutableContext(),
+ context_seeds[index]);
+
+ thread_snapshots.push_back(thread_snapshot);
+ }
+
+ auto thread_list_writer = make_scoped_ptr(new MinidumpThreadListWriter());
+ auto memory_list_writer = make_scoped_ptr(new MinidumpMemoryListWriter());
+ thread_list_writer->SetMemoryListWriter(memory_list_writer.get());
+ MinidumpThreadIDMap thread_id_map;
+ thread_list_writer->InitializeFromSnapshot(thread_snapshots, &thread_id_map);
+
+ MinidumpFileWriter minidump_file_writer;
+ minidump_file_writer.AddStream(thread_list_writer.Pass());
+ minidump_file_writer.AddStream(memory_list_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_THREAD_LIST* thread_list = nullptr;
+ const MINIDUMP_MEMORY_LIST* memory_list = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ GetThreadListStream(string_file.string(), &thread_list, &memory_list));
+
+ ASSERT_EQ(3u, thread_list->NumberOfThreads);
+ ASSERT_EQ(2u, memory_list->NumberOfMemoryRanges);
+
+ size_t memory_index = 0;
+ for (size_t index = 0; index < thread_list->NumberOfThreads; ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index));
+
+ const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack = nullptr;
+ const MINIDUMP_MEMORY_DESCRIPTOR** observed_stack_p =
+ expect_threads[index].Stack.Memory.DataSize ? &observed_stack : nullptr;
+ const MinidumpContextType* observed_context = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ ExpectThread(&expect_threads[index],
+ &thread_list->Threads[index],
+ string_file.string(),
+ observed_stack_p,
+ reinterpret_cast<const void**>(&observed_context)));
+
+ ASSERT_NO_FATAL_FAILURE(Traits::ExpectMinidumpContext(
+ context_seeds[index], observed_context, true));
+
+ if (observed_stack_p) {
+ ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptorAndContents(
+ &expect_threads[index].Stack,
+ observed_stack,
+ string_file.string(),
+ memory_values[index],
+ index == thread_list->NumberOfThreads - 1));
+
+ ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor(
+ observed_stack, &memory_list->MemoryRanges[memory_index]));
+
+ ++memory_index;
+ }
+ }
+}
+
+TEST(MinidumpThreadWriter, InitializeFromSnapshot_x86) {
+ RunInitializeFromSnapshotTest<InitializeFromSnapshotX86Traits>(false);
+}
+
+TEST(MinidumpThreadWriter, InitializeFromSnapshot_AMD64) {
+ RunInitializeFromSnapshotTest<InitializeFromSnapshotAMD64Traits>(false);
+}
+
+TEST(MinidumpThreadWriter, InitializeFromSnapshot_ThreadIDCollision) {
+ RunInitializeFromSnapshotTest<InitializeFromSnapshotX86Traits>(true);
+}
+
+TEST(MinidumpThreadWriterDeathTest, NoContext) {
+ MinidumpFileWriter minidump_file_writer;
+ auto thread_list_writer = make_scoped_ptr(new MinidumpThreadListWriter());
+
+ auto thread_writer = make_scoped_ptr(new MinidumpThreadWriter());
+
+ thread_list_writer->AddThread(thread_writer.Pass());
+ minidump_file_writer.AddStream(thread_list_writer.Pass());
+
+ StringFile string_file;
+ ASSERT_DEATH_CHECK(minidump_file_writer.WriteEverything(&string_file),
+ "context_");
+}
+
+TEST(MinidumpThreadWriterDeathTest, InitializeFromSnapshot_NoContext) {
+ ASSERT_DEATH_CHECK(
+ RunInitializeFromSnapshotTest<InitializeFromSnapshotNoContextTraits>(
+ false), "context_");
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_writable.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_writable.cc
new file mode 100644
index 00000000000..8b73906080d
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_writable.cc
@@ -0,0 +1,269 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_writable.h"
+
+#include <stdint.h>
+
+#include <limits>
+
+#include "base/logging.h"
+#include "util/file/file_writer.h"
+#include "util/numeric/safe_assignment.h"
+
+namespace {
+
+const size_t kMaximumAlignment = 16;
+
+} // namespace
+
+namespace crashpad {
+namespace internal {
+
+MinidumpWritable::~MinidumpWritable() {
+}
+
+bool MinidumpWritable::WriteEverything(FileWriterInterface* file_writer) {
+ DCHECK_EQ(state_, kStateMutable);
+
+ if (!Freeze()) {
+ return false;
+ }
+
+ DCHECK_EQ(state_, kStateFrozen);
+
+ FileOffset offset = 0;
+ std::vector<MinidumpWritable*> write_sequence;
+ size_t size = WillWriteAtOffset(kPhaseEarly, &offset, &write_sequence);
+ if (size == kInvalidSize) {
+ return false;
+ }
+
+ offset += size;
+ if (WillWriteAtOffset(kPhaseLate, &offset, &write_sequence) == kInvalidSize) {
+ return false;
+ }
+
+ DCHECK_EQ(state_, kStateWritable);
+ DCHECK_EQ(write_sequence.front(), this);
+
+ for (MinidumpWritable* writable : write_sequence) {
+ if (!writable->WritePaddingAndObject(file_writer)) {
+ return false;
+ }
+ }
+
+ DCHECK_EQ(state_, kStateWritten);
+
+ return true;
+}
+
+void MinidumpWritable::RegisterRVA(RVA* rva) {
+ DCHECK_LE(state_, kStateFrozen);
+
+ registered_rvas_.push_back(rva);
+}
+
+void MinidumpWritable::RegisterLocationDescriptor(
+ MINIDUMP_LOCATION_DESCRIPTOR* location_descriptor) {
+ DCHECK_LE(state_, kStateFrozen);
+
+ registered_location_descriptors_.push_back(location_descriptor);
+}
+
+const size_t MinidumpWritable::kInvalidSize =
+ std::numeric_limits<size_t>::max();
+
+MinidumpWritable::MinidumpWritable()
+ : registered_rvas_(),
+ registered_location_descriptors_(),
+ leading_pad_bytes_(0),
+ state_(kStateMutable) {
+}
+
+bool MinidumpWritable::Freeze() {
+ DCHECK_EQ(state_, kStateMutable);
+ state_ = kStateFrozen;
+
+ std::vector<MinidumpWritable*> children = Children();
+ for (MinidumpWritable* child : children) {
+ if (!child->Freeze()) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+size_t MinidumpWritable::Alignment() {
+ DCHECK_GE(state_, kStateFrozen);
+
+ return 4;
+}
+
+std::vector<MinidumpWritable*> MinidumpWritable::Children() {
+ DCHECK_GE(state_, kStateFrozen);
+
+ return std::vector<MinidumpWritable*>();
+}
+
+MinidumpWritable::Phase MinidumpWritable::WritePhase() {
+ return kPhaseEarly;
+}
+
+size_t MinidumpWritable::WillWriteAtOffset(
+ Phase phase,
+ FileOffset* offset,
+ std::vector<MinidumpWritable*>* write_sequence) {
+ FileOffset local_offset = *offset;
+ CHECK_GE(local_offset, 0);
+
+ size_t leading_pad_bytes_this_phase;
+ size_t size;
+ if (phase == WritePhase()) {
+ DCHECK_EQ(state_, kStateFrozen);
+
+ // Add this object to the sequence of MinidumpWritable objects to be
+ // written.
+ write_sequence->push_back(this);
+
+ size = SizeOfObject();
+
+ if (size > 0) {
+ // Honor this object’s request to be aligned to a specific byte boundary.
+ // Once the alignment is corrected, this object knows exactly what file
+ // offset it will be written at.
+ size_t alignment = Alignment();
+ CHECK_LE(alignment, kMaximumAlignment);
+
+ leading_pad_bytes_this_phase =
+ (alignment - (local_offset % alignment)) % alignment;
+ local_offset += leading_pad_bytes_this_phase;
+ *offset = local_offset;
+ } else {
+ // If the object is size 0, alignment is of no concern.
+ leading_pad_bytes_this_phase = 0;
+ }
+ leading_pad_bytes_ = leading_pad_bytes_this_phase;
+
+ // Now that the file offset that this object will be written at is known,
+ // let the subclass implementation know in case it’s interested.
+ if (!WillWriteAtOffsetImpl(local_offset)) {
+ return kInvalidSize;
+ }
+
+ // Populate the RVA fields in other objects that have registered to point to
+ // this one. Typically, a parent object will have registered to point to its
+ // children, but this can also occur where no parent-child relationship
+ // exists.
+ if (!registered_rvas_.empty() ||
+ !registered_location_descriptors_.empty()) {
+ RVA local_rva;
+ if (!AssignIfInRange(&local_rva, local_offset)) {
+ LOG(ERROR) << "offset " << local_offset << " out of range";
+ return kInvalidSize;
+ }
+
+ for (RVA* rva : registered_rvas_) {
+ *rva = local_rva;
+ }
+
+ if (!registered_location_descriptors_.empty()) {
+ decltype(registered_location_descriptors_[0]->DataSize) local_size;
+ if (!AssignIfInRange(&local_size, size)) {
+ LOG(ERROR) << "size " << size << " out of range";
+ return kInvalidSize;
+ }
+
+ for (MINIDUMP_LOCATION_DESCRIPTOR* location_descriptor :
+ registered_location_descriptors_) {
+ location_descriptor->DataSize = local_size;
+ location_descriptor->Rva = local_rva;
+ }
+ }
+ }
+
+ // This object is now considered writable. However, if it contains RVA or
+ // MINIDUMP_LOCATION_DESCRIPTOR fields, they may not be fully updated yet,
+ // because it’s the repsonsibility of these fields’ pointees to update them.
+ // Once WillWriteAtOffset has completed running for both phases on an entire
+ // tree, and the entire tree has moved into kStateFrozen, all RVA and
+ // MINIDUMP_LOCATION_DESCRIPTOR fields within that tree will be populated.
+ state_ = kStateWritable;
+ } else {
+ if (phase == kPhaseEarly) {
+ DCHECK_EQ(state_, kStateFrozen);
+ } else {
+ DCHECK_EQ(state_, kStateWritable);
+ }
+
+ size = 0;
+ leading_pad_bytes_this_phase = 0;
+ }
+
+ // Loop over children regardless of whether this object itself will write
+ // during this phase. An object’s children are not required to be written
+ // during the same phase as their parent.
+ std::vector<MinidumpWritable*> children = Children();
+ for (MinidumpWritable* child : children) {
+ // Use “auto” here because it’s impossible to know whether size_t (size) or
+ // FileOffset (local_offset) is the wider type, and thus what type the
+ // result of adding these two variables will have.
+ auto unaligned_child_offset = local_offset + size;
+ FileOffset child_offset;
+ if (!AssignIfInRange(&child_offset, unaligned_child_offset)) {
+ LOG(ERROR) << "offset " << unaligned_child_offset << " out of range";
+ return kInvalidSize;
+ }
+
+ size_t child_size =
+ child->WillWriteAtOffset(phase, &child_offset, write_sequence);
+ if (child_size == kInvalidSize) {
+ return kInvalidSize;
+ }
+
+ size += child_size;
+ }
+
+ return leading_pad_bytes_this_phase + size;
+}
+
+bool MinidumpWritable::WillWriteAtOffsetImpl(FileOffset offset) {
+ return true;
+}
+
+bool MinidumpWritable::WritePaddingAndObject(FileWriterInterface* file_writer) {
+ DCHECK_EQ(state_, kStateWritable);
+
+ // The number of elements in kZeroes must be at least one less than the
+ // maximum Alignment() ever encountered.
+ const uint8_t kZeroes[kMaximumAlignment - 1] = {};
+ DCHECK_LE(leading_pad_bytes_, arraysize(kZeroes));
+
+ if (leading_pad_bytes_) {
+ if (!file_writer->Write(&kZeroes, leading_pad_bytes_)) {
+ return false;
+ }
+ }
+
+ if (!WriteObject(file_writer)) {
+ return false;
+ }
+
+ state_ = kStateWritten;
+ return true;
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_writable.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_writable.h
new file mode 100644
index 00000000000..9e7cf71d3dd
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_writable.h
@@ -0,0 +1,279 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_MINIDUMP_MINIDUMP_WRITABLE_H_
+#define CRASHPAD_MINIDUMP_MINIDUMP_WRITABLE_H_
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <sys/types.h>
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "util/file/file_io.h"
+
+namespace crashpad {
+
+class FileWriterInterface;
+
+namespace internal {
+
+//! \brief The base class for all content that might be written to a minidump
+//! file.
+class MinidumpWritable {
+ public:
+ virtual ~MinidumpWritable();
+
+ //! \brief Writes an object and all of its children to a minidump file.
+ //!
+ //! Use this on the root object of a tree of MinidumpWritable objects,
+ //! typically on a MinidumpFileWriter object.
+ //!
+ //! \param[in] file_writer The file writer to receive the minidump file’s
+ //! content.
+ //!
+ //! \return `true` on success. `false` on failure, with an appropriate message
+ //! logged.
+ //!
+ //! \note Valid in #kStateMutable, and transitions the object and the entire
+ //! tree beneath it through all states to #kStateWritten.
+ //!
+ //! \note This method should rarely be overridden.
+ virtual bool WriteEverything(FileWriterInterface* file_writer);
+
+ //! \brief Registers a file offset pointer as one that should point to the
+ //! object on which this method is called.
+ //!
+ //! Once the file offset at which an object will be written is known (when it
+ //! enters #kStateWritable), registered RVA pointers will be updated.
+ //!
+ //! \param[in] rva A pointer to storage for the file offset that should
+ //! contain this object’s writable file offset, once it is known.
+ //!
+ //! \note Valid in #kStateFrozen or any preceding state.
+ //
+ // This is public instead of protected because objects of derived classes need
+ // to be able to register their own pointers with distinct objects.
+ void RegisterRVA(RVA* rva);
+
+ //! \brief Registers a location descriptor as one that should point to the
+ //! object on which this method is called.
+ //!
+ //! Once an object’s size and the file offset at it will be written is known
+ //! (when it enters #kStateFrozen), the relevant data in registered location
+ //! descriptors will be updated.
+ //!
+ //! \param[in] location_descriptor A pointer to a location descriptor that
+ //! should contain this object’s writable size and file offset, once they
+ //! are known.
+ //!
+ //! \note Valid in #kStateFrozen or any preceding state.
+ //
+ // This is public instead of protected because objects of derived classes need
+ // to be able to register their own pointers with distinct objects.
+ void RegisterLocationDescriptor(
+ MINIDUMP_LOCATION_DESCRIPTOR* location_descriptor);
+
+ protected:
+ //! \brief Identifies the state of an object.
+ //!
+ //! Objects will normally transition through each of these states as they are
+ //! created, populated with data, and then written to a minidump file.
+ enum State {
+ //! \brief The object’s properties can be modified.
+ kStateMutable = 0,
+
+ //! \brief The object is “frozen”.
+ //!
+ //! Its properties cannot be modified. Pointers to file offsets of other
+ //! structures may not yet be valid.
+ kStateFrozen,
+
+ //! \brief The object is writable.
+ //!
+ //! The file offset at which it will be written is known. Pointers to file
+ //! offsets of other structures are valid when all objects in a tree are in
+ //! this state.
+ kStateWritable,
+
+ //! \brief The object has been written to a minidump file.
+ kStateWritten,
+ };
+
+ //! \brief Identifies the phase during which an object will be written to a
+ //! minidump file.
+ enum Phase {
+ //! \brief Objects that are written to a minidump file “early”.
+ //!
+ //! The normal sequence is for an object to write itself and then write all
+ //! of its children.
+ kPhaseEarly = 0,
+
+ //! \brief Objects that are written to a minidump file “late”.
+ //!
+ //! Some objects, such as those capturing memory region snapshots, are
+ //! written to minidump files after all other objects. This “late” phase
+ //! identifies such objects. This is useful to improve spatial locality in
+ //! in minidump files in accordance with expected access patterns: unlike
+ //! most other data, memory snapshots are large and the entire snapshots do
+ //! not need to be consulted in order to process a minidump file.
+ kPhaseLate,
+ };
+
+ //! \brief A size value used to signal failure by methods that return
+ //! `size_t`.
+ static const size_t kInvalidSize;
+
+ MinidumpWritable();
+
+ //! \brief The state of the object.
+ State state() const { return state_; }
+
+ //! \brief Transitions the object from #kStateMutable to #kStateFrozen.
+ //!
+ //! The default implementation marks the object as frozen and recursively
+ //! calls Freeze() on all of its children. Subclasses may override this method
+ //! to perform processing that should only be done once callers have finished
+ //! populating an object with data. Typically, a subclass implementation would
+ //! call RegisterRVA() or RegisterLocationDescriptor() on other objects as
+ //! appropriate, because at the time Freeze() runs, the in-memory locations of
+ //! RVAs and location descriptors are known and will not change for the
+ //! remaining duration of an object’s lifetime.
+ //!
+ //! \return `true` on success. `false` on failure, with an appropriate message
+ //! logged.
+ virtual bool Freeze();
+
+ //! \brief Returns the amount of space that this object will consume when
+ //! written to a minidump file, in bytes, not including any leading or
+ //! trailing padding necessary to maintain proper alignment.
+ //!
+ //! \note Valid in #kStateFrozen or any subsequent state.
+ virtual size_t SizeOfObject() = 0;
+
+ //! \brief Returns the object’s desired byte-boundary alignment.
+ //!
+ //! The default implementation returns `4`. Subclasses may override this as
+ //! needed.
+ //!
+ //! \note Valid in #kStateFrozen or any subsequent state.
+ virtual size_t Alignment();
+
+ //! \brief Returns the object’s children.
+ //!
+ //! \note Valid in #kStateFrozen or any subsequent state.
+ virtual std::vector<MinidumpWritable*> Children();
+
+ //! \brief Returns the object’s desired write phase.
+ //!
+ //! The default implementation returns #kPhaseEarly. Subclasses may override
+ //! this method to alter their write phase.
+ //!
+ //! \note Valid in any state.
+ virtual Phase WritePhase();
+
+ //! \brief Prepares the object to be written at a known file offset,
+ //! transitioning it from #kStateFrozen to #kStateWritable.
+ //!
+ //! This method is responsible for determining the final file offset of the
+ //! object, which may be increased from \a offset to meet alignment
+ //! requirements. It calls WillWriteAtOffsetImpl() for the benefit of
+ //! subclasses. It populates all RVAs and location descriptors registered with
+ //! it via RegisterRVA() and RegisterLocationDescriptor(). It also recurses
+ //! into all known children.
+ //!
+ //! \param[in] phase The phase during which the object will be written. If
+ //! this does not match Phase(), processing is suppressed, although
+ //! recursive processing will still occur on all children. This addresses
+ //! the case where parents and children do not write in the same phase.
+ //! \param[in] offset The file offset at which the object will be written. The
+ //! offset may need to be adjusted for alignment.
+ //! \param[out] write_sequence This object will append itself to this list,
+ //! such that on return from a recursive tree of WillWriteAtOffset()
+ //! calls, elements of the vector will be organized in the sequence that
+ //! the objects will be written to the minidump file.
+ //!
+ //! \return The file size consumed by this object and all children, including
+ //! any padding inserted to meet alignment requirements. On failure,
+ //! #kInvalidSize, with an appropriate message logged.
+ //!
+ //! \note This method cannot be overridden. Subclasses that need to perform
+ //! processing when an object transitions to #kStateWritable should
+ //! implement WillWriteAtOffsetImpl(), which is called by this method.
+ size_t WillWriteAtOffset(Phase phase,
+ FileOffset* offset,
+ std::vector<MinidumpWritable*>* write_sequence);
+
+ //! \brief Called once an object’s writable file offset is determined, as it
+ //! transitions into #kStateWritable.
+ //!
+ //! Subclasses can override this method if they need to provide additional
+ //! processing once their writable file offset is known. Typically, this will
+ //! be done by subclasses that handle certain RVAs themselves instead of using
+ //! the RegisterRVA() interface.
+ //!
+ //! \param[in] offset The file offset at which the object will be written. The
+ //! value passed to this method will already have been adjusted to meet
+ //! alignment requirements.
+ //!
+ //! \return `true` on success. `false` on error, indicating that the minidump
+ //! file should not be written.
+ //!
+ //! \note Valid in #kStateFrozen. The object will transition to
+ //! #kStateWritable after this method returns.
+ virtual bool WillWriteAtOffsetImpl(FileOffset offset);
+
+ //! \brief Writes the object, transitioning it from #kStateWritable to
+ //! #kStateWritten.
+ //!
+ //! Writes any padding necessary to meet alignment requirements, and then
+ //! calls WriteObject() to write the object’s content.
+ //!
+ //! \param[in] file_writer The file writer to receive the object’s content.
+ //!
+ //! \return `true` on success. `false` on error with an appropriate message
+ //! logged.
+ //!
+ //! \note This method cannot be overridden. Subclasses must override
+ //! WriteObject().
+ bool WritePaddingAndObject(FileWriterInterface* file_writer);
+
+ //! \brief Writes the object’s content.
+ //!
+ //! \param[in] file_writer The file writer to receive the object’s content.
+ //!
+ //! \return `true` on success. `false` on error, indicating that the content
+ //! could not be written to the minidump file.
+ //!
+ //! \note Valid in #kStateWritable. The object will transition to
+ //! #kStateWritten after this method returns.
+ virtual bool WriteObject(FileWriterInterface* file_writer) = 0;
+
+ private:
+ std::vector<RVA*> registered_rvas_; // weak
+
+ // weak
+ std::vector<MINIDUMP_LOCATION_DESCRIPTOR*> registered_location_descriptors_;
+
+ size_t leading_pad_bytes_;
+ State state_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpWritable);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_MINIDUMP_MINIDUMP_WRITABLE_H_
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_writable_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_writable_test.cc
new file mode 100644
index 00000000000..38d97de34da
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_writable_test.cc
@@ -0,0 +1,837 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_writable.h"
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "gtest/gtest.h"
+#include "util/file/string_file.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+class BaseTestMinidumpWritable : public crashpad::internal::MinidumpWritable {
+ public:
+ BaseTestMinidumpWritable()
+ : MinidumpWritable(),
+ children_(),
+ expected_offset_(-1),
+ alignment_(0),
+ phase_(kPhaseEarly),
+ has_alignment_(false),
+ has_phase_(false),
+ verified_(false) {}
+
+ ~BaseTestMinidumpWritable() { EXPECT_TRUE(verified_); }
+
+ void SetAlignment(size_t alignment) {
+ alignment_ = alignment;
+ has_alignment_ = true;
+ }
+
+ void AddChild(BaseTestMinidumpWritable* child) { children_.push_back(child); }
+
+ void SetPhaseLate() {
+ phase_ = kPhaseLate;
+ has_phase_ = true;
+ }
+
+ void Verify() {
+ verified_ = true;
+ EXPECT_EQ(kStateWritten, state());
+ for (BaseTestMinidumpWritable* child : children_) {
+ child->Verify();
+ }
+ }
+
+ protected:
+ bool Freeze() override {
+ EXPECT_EQ(kStateMutable, state());
+ bool rv = MinidumpWritable::Freeze();
+ EXPECT_TRUE(rv);
+ EXPECT_EQ(kStateFrozen, state());
+ return rv;
+ }
+
+ size_t Alignment() override {
+ EXPECT_GE(state(), kStateFrozen);
+ return has_alignment_ ? alignment_ : MinidumpWritable::Alignment();
+ }
+
+ std::vector<MinidumpWritable*> Children() override {
+ EXPECT_GE(state(), kStateFrozen);
+ if (!children_.empty()) {
+ std::vector<MinidumpWritable*> children;
+ for (BaseTestMinidumpWritable* child : children_) {
+ children.push_back(child);
+ }
+ return children;
+ }
+ return MinidumpWritable::Children();
+ }
+
+ Phase WritePhase() override {
+ return has_phase_ ? phase_ : MinidumpWritable::Phase();
+ }
+
+ bool WillWriteAtOffsetImpl(FileOffset offset) override {
+ EXPECT_EQ(state(), kStateFrozen);
+ expected_offset_ = offset;
+ bool rv = MinidumpWritable::WillWriteAtOffsetImpl(offset);
+ EXPECT_TRUE(rv);
+ return rv;
+ }
+
+ bool WriteObject(FileWriterInterface* file_writer) override {
+ EXPECT_EQ(state(), kStateWritable);
+ EXPECT_EQ(expected_offset_, file_writer->Seek(0, SEEK_CUR));
+
+ // Subclasses must override this.
+ return false;
+ }
+
+ private:
+ std::vector<BaseTestMinidumpWritable*> children_;
+ FileOffset expected_offset_;
+ size_t alignment_;
+ Phase phase_;
+ bool has_alignment_;
+ bool has_phase_;
+ bool verified_;
+
+ DISALLOW_COPY_AND_ASSIGN(BaseTestMinidumpWritable);
+};
+
+class TestStringMinidumpWritable final : public BaseTestMinidumpWritable {
+ public:
+ TestStringMinidumpWritable() : BaseTestMinidumpWritable(), data_() {}
+
+ ~TestStringMinidumpWritable() {}
+
+ void SetData(const std::string& string) { data_ = string; }
+
+ protected:
+ size_t SizeOfObject() override {
+ EXPECT_GE(state(), kStateFrozen);
+ return data_.size();
+ }
+
+ bool WriteObject(FileWriterInterface* file_writer) override {
+ BaseTestMinidumpWritable::WriteObject(file_writer);
+ bool rv = file_writer->Write(&data_[0], data_.size());
+ EXPECT_TRUE(rv);
+ return rv;
+ }
+
+ private:
+ std::string data_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestStringMinidumpWritable);
+};
+
+TEST(MinidumpWritable, MinidumpWritable) {
+ StringFile string_file;
+
+ {
+ SCOPED_TRACE("empty");
+ string_file.Reset();
+ TestStringMinidumpWritable string_writable;
+ EXPECT_TRUE(string_writable.WriteEverything(&string_file));
+ EXPECT_TRUE(string_file.string().empty());
+ string_writable.Verify();
+ }
+
+ {
+ SCOPED_TRACE("childless");
+ string_file.Reset();
+ TestStringMinidumpWritable string_writable;
+ string_writable.SetData("a");
+ EXPECT_TRUE(string_writable.WriteEverything(&string_file));
+ EXPECT_EQ(1u, string_file.string().size());
+ EXPECT_EQ("a", string_file.string());
+ string_writable.Verify();
+ }
+
+ {
+ SCOPED_TRACE("parent-child");
+ string_file.Reset();
+ TestStringMinidumpWritable parent;
+ parent.SetData("b");
+ TestStringMinidumpWritable child;
+ child.SetData("c");
+ parent.AddChild(&child);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+ EXPECT_EQ(5u, string_file.string().size());
+ EXPECT_EQ(std::string("b\0\0\0c", 5), string_file.string());
+ parent.Verify();
+ }
+
+ {
+ SCOPED_TRACE("base alignment 2");
+ string_file.Reset();
+ TestStringMinidumpWritable parent;
+ parent.SetData("de");
+ TestStringMinidumpWritable child;
+ child.SetData("f");
+ parent.AddChild(&child);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+ EXPECT_EQ(5u, string_file.string().size());
+ EXPECT_EQ(std::string("de\0\0f", 5), string_file.string());
+ parent.Verify();
+ }
+
+ {
+ SCOPED_TRACE("base alignment 3");
+ string_file.Reset();
+ TestStringMinidumpWritable parent;
+ parent.SetData("ghi");
+ TestStringMinidumpWritable child;
+ child.SetData("j");
+ parent.AddChild(&child);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+ EXPECT_EQ(5u, string_file.string().size());
+ EXPECT_EQ(std::string("ghi\0j", 5), string_file.string());
+ parent.Verify();
+ }
+
+ {
+ SCOPED_TRACE("base alignment 4");
+ string_file.Reset();
+ TestStringMinidumpWritable parent;
+ parent.SetData("klmn");
+ TestStringMinidumpWritable child;
+ child.SetData("o");
+ parent.AddChild(&child);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+ EXPECT_EQ(5u, string_file.string().size());
+ EXPECT_EQ("klmno", string_file.string());
+ parent.Verify();
+ }
+
+ {
+ SCOPED_TRACE("base alignment 5");
+ string_file.Reset();
+ TestStringMinidumpWritable parent;
+ parent.SetData("pqrst");
+ TestStringMinidumpWritable child;
+ child.SetData("u");
+ parent.AddChild(&child);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+ EXPECT_EQ(9u, string_file.string().size());
+ EXPECT_EQ(std::string("pqrst\0\0\0u", 9), string_file.string());
+ parent.Verify();
+ }
+
+ {
+ SCOPED_TRACE("two children");
+ string_file.Reset();
+ TestStringMinidumpWritable parent;
+ parent.SetData("parent");
+ TestStringMinidumpWritable child_0;
+ child_0.SetData("child_0");
+ parent.AddChild(&child_0);
+ TestStringMinidumpWritable child_1;
+ child_1.SetData("child_1");
+ parent.AddChild(&child_1);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+ EXPECT_EQ(23u, string_file.string().size());
+ EXPECT_EQ(std::string("parent\0\0child_0\0child_1", 23), string_file.string());
+ parent.Verify();
+ }
+
+ {
+ SCOPED_TRACE("grandchild");
+ string_file.Reset();
+ TestStringMinidumpWritable parent;
+ parent.SetData("parent");
+ TestStringMinidumpWritable child;
+ child.SetData("child");
+ parent.AddChild(&child);
+ TestStringMinidumpWritable grandchild;
+ grandchild.SetData("grandchild");
+ child.AddChild(&grandchild);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+ EXPECT_EQ(26u, string_file.string().size());
+ EXPECT_EQ(std::string("parent\0\0child\0\0\0grandchild", 26),
+ string_file.string());
+ parent.Verify();
+ }
+
+ {
+ SCOPED_TRACE("grandchild with empty parent");
+ string_file.Reset();
+ TestStringMinidumpWritable parent;
+ TestStringMinidumpWritable child;
+ child.SetData("child");
+ parent.AddChild(&child);
+ TestStringMinidumpWritable grandchild;
+ grandchild.SetData("grandchild");
+ child.AddChild(&grandchild);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+ EXPECT_EQ(18u, string_file.string().size());
+ EXPECT_EQ(std::string("child\0\0\0grandchild", 18), string_file.string());
+ parent.Verify();
+ }
+
+ {
+ SCOPED_TRACE("grandchild with empty child");
+ string_file.Reset();
+ TestStringMinidumpWritable parent;
+ parent.SetData("parent");
+ TestStringMinidumpWritable child;
+ parent.AddChild(&child);
+ TestStringMinidumpWritable grandchild;
+ grandchild.SetData("grandchild");
+ child.AddChild(&grandchild);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+ EXPECT_EQ(18u, string_file.string().size());
+ EXPECT_EQ(std::string("parent\0\0grandchild", 18), string_file.string());
+ parent.Verify();
+ }
+
+ {
+ SCOPED_TRACE("grandchild with empty grandchild");
+ string_file.Reset();
+ TestStringMinidumpWritable parent;
+ parent.SetData("parent");
+ TestStringMinidumpWritable child;
+ child.SetData("child");
+ parent.AddChild(&child);
+ TestStringMinidumpWritable grandchild;
+ child.AddChild(&grandchild);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+ EXPECT_EQ(13u, string_file.string().size());
+ EXPECT_EQ(std::string("parent\0\0child", 13), string_file.string());
+ parent.Verify();
+ }
+
+ {
+ SCOPED_TRACE("grandchild with late-phase grandchild");
+ string_file.Reset();
+ TestStringMinidumpWritable parent;
+ parent.SetData("parent");
+ TestStringMinidumpWritable child;
+ child.SetData("child");
+ parent.AddChild(&child);
+ TestStringMinidumpWritable grandchild;
+ grandchild.SetData("grandchild");
+ grandchild.SetPhaseLate();
+ child.AddChild(&grandchild);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+ EXPECT_EQ(26u, string_file.string().size());
+ EXPECT_EQ(std::string("parent\0\0child\0\0\0grandchild", 26),
+ string_file.string());
+ parent.Verify();
+ }
+
+ {
+ SCOPED_TRACE("grandchild with late-phase child");
+ string_file.Reset();
+ TestStringMinidumpWritable parent;
+ parent.SetData("parent");
+ TestStringMinidumpWritable child;
+ child.SetData("child");
+ child.SetPhaseLate();
+ parent.AddChild(&child);
+ TestStringMinidumpWritable grandchild;
+ grandchild.SetData("grandchild");
+ child.AddChild(&grandchild);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+ EXPECT_EQ(25u, string_file.string().size());
+ EXPECT_EQ(std::string("parent\0\0grandchild\0\0child", 25),
+ string_file.string());
+ parent.Verify();
+ }
+
+ {
+ SCOPED_TRACE("family tree");
+ string_file.Reset();
+ TestStringMinidumpWritable parent;
+ parent.SetData("P..");
+ TestStringMinidumpWritable child_0;
+ child_0.SetData("C0.");
+ parent.AddChild(&child_0);
+ TestStringMinidumpWritable child_1;
+ child_1.SetData("C1.");
+ parent.AddChild(&child_1);
+ TestStringMinidumpWritable grandchild_00;
+ grandchild_00.SetData("G00");
+ child_0.AddChild(&grandchild_00);
+ TestStringMinidumpWritable grandchild_01;
+ grandchild_01.SetData("G01");
+ child_0.AddChild(&grandchild_01);
+ TestStringMinidumpWritable grandchild_10;
+ grandchild_10.SetData("G10");
+ child_1.AddChild(&grandchild_10);
+ TestStringMinidumpWritable grandchild_11;
+ grandchild_11.SetData("G11");
+ child_1.AddChild(&grandchild_11);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+ EXPECT_EQ(27u, string_file.string().size());
+ EXPECT_EQ(std::string("P..\0C0.\0G00\0G01\0C1.\0G10\0G11", 27),
+ string_file.string());
+ parent.Verify();
+ }
+
+ {
+ SCOPED_TRACE("family tree with C0 late");
+ string_file.Reset();
+ TestStringMinidumpWritable parent;
+ parent.SetData("P..");
+ TestStringMinidumpWritable child_0;
+ child_0.SetData("C0.");
+ child_0.SetPhaseLate();
+ parent.AddChild(&child_0);
+ TestStringMinidumpWritable child_1;
+ child_1.SetData("C1.");
+ parent.AddChild(&child_1);
+ TestStringMinidumpWritable grandchild_00;
+ grandchild_00.SetData("G00");
+ child_0.AddChild(&grandchild_00);
+ TestStringMinidumpWritable grandchild_01;
+ grandchild_01.SetData("G01");
+ child_0.AddChild(&grandchild_01);
+ TestStringMinidumpWritable grandchild_10;
+ grandchild_10.SetData("G10");
+ child_1.AddChild(&grandchild_10);
+ TestStringMinidumpWritable grandchild_11;
+ grandchild_11.SetData("G11");
+ child_1.AddChild(&grandchild_11);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+ EXPECT_EQ(27u, string_file.string().size());
+ EXPECT_EQ(std::string("P..\0G00\0G01\0C1.\0G10\0G11\0C0.", 27),
+ string_file.string());
+ parent.Verify();
+ }
+
+ {
+ SCOPED_TRACE("family tree with G0 late");
+ string_file.Reset();
+ TestStringMinidumpWritable parent;
+ parent.SetData("P..");
+ TestStringMinidumpWritable child_0;
+ child_0.SetData("C0.");
+ parent.AddChild(&child_0);
+ TestStringMinidumpWritable child_1;
+ child_1.SetData("C1.");
+ parent.AddChild(&child_1);
+ TestStringMinidumpWritable grandchild_00;
+ grandchild_00.SetData("G00");
+ grandchild_00.SetPhaseLate();
+ child_0.AddChild(&grandchild_00);
+ TestStringMinidumpWritable grandchild_01;
+ grandchild_01.SetData("G01");
+ grandchild_01.SetPhaseLate();
+ child_0.AddChild(&grandchild_01);
+ TestStringMinidumpWritable grandchild_10;
+ grandchild_10.SetData("G10");
+ child_1.AddChild(&grandchild_10);
+ TestStringMinidumpWritable grandchild_11;
+ grandchild_11.SetData("G11");
+ child_1.AddChild(&grandchild_11);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+ EXPECT_EQ(27u, string_file.string().size());
+ EXPECT_EQ(std::string("P..\0C0.\0C1.\0G10\0G11\0G00\0G01", 27),
+ string_file.string());
+ parent.Verify();
+ }
+
+ {
+ SCOPED_TRACE("align 1");
+ string_file.Reset();
+ TestStringMinidumpWritable parent;
+ parent.SetData("p");
+ TestStringMinidumpWritable child;
+ child.SetData("c");
+ child.SetAlignment(1);
+ parent.AddChild(&child);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+ EXPECT_EQ(2u, string_file.string().size());
+ EXPECT_EQ("pc", string_file.string());
+ parent.Verify();
+ }
+
+ {
+ SCOPED_TRACE("align 2");
+ string_file.Reset();
+ TestStringMinidumpWritable parent;
+ parent.SetData("p");
+ TestStringMinidumpWritable child;
+ child.SetData("c");
+ child.SetAlignment(2);
+ parent.AddChild(&child);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+ EXPECT_EQ(3u, string_file.string().size());
+ EXPECT_EQ(std::string("p\0c", 3), string_file.string());
+ parent.Verify();
+ }
+}
+
+class TestRVAMinidumpWritable final : public BaseTestMinidumpWritable {
+ public:
+ TestRVAMinidumpWritable() : BaseTestMinidumpWritable(), rva_() {}
+
+ ~TestRVAMinidumpWritable() {}
+
+ void SetRVA(MinidumpWritable* other) { other->RegisterRVA(&rva_); }
+
+ protected:
+ size_t SizeOfObject() override {
+ EXPECT_GE(state(), kStateFrozen);
+ return sizeof(rva_);
+ }
+
+ bool WriteObject(FileWriterInterface* file_writer) override {
+ BaseTestMinidumpWritable::WriteObject(file_writer);
+ EXPECT_TRUE(file_writer->Write(&rva_, sizeof(rva_)));
+ return true;
+ }
+
+ private:
+ RVA rva_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestRVAMinidumpWritable);
+};
+
+RVA RVAAtIndex(const std::string& string, size_t index) {
+ return *reinterpret_cast<const RVA*>(&string[index * sizeof(RVA)]);
+}
+
+TEST(MinidumpWritable, RVA) {
+ StringFile string_file;
+
+ {
+ SCOPED_TRACE("unset");
+ string_file.Reset();
+ TestRVAMinidumpWritable rva_writable;
+ EXPECT_TRUE(rva_writable.WriteEverything(&string_file));
+
+ ASSERT_EQ(sizeof(RVA), string_file.string().size());
+ EXPECT_EQ(0 * sizeof(RVA), RVAAtIndex(string_file.string(), 0));
+ rva_writable.Verify();
+ }
+
+ {
+ SCOPED_TRACE("self");
+ string_file.Reset();
+ TestRVAMinidumpWritable rva_writable;
+ rva_writable.SetRVA(&rva_writable);
+ EXPECT_TRUE(rva_writable.WriteEverything(&string_file));
+
+ ASSERT_EQ(sizeof(RVA), string_file.string().size());
+ EXPECT_EQ(0 * sizeof(RVA), RVAAtIndex(string_file.string(), 0));
+ rva_writable.Verify();
+ }
+
+ {
+ SCOPED_TRACE("parent-child self");
+ string_file.Reset();
+ TestRVAMinidumpWritable parent;
+ parent.SetRVA(&parent);
+ TestRVAMinidumpWritable child;
+ child.SetRVA(&child);
+ parent.AddChild(&child);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+
+ ASSERT_EQ(2 * sizeof(RVA), string_file.string().size());
+ EXPECT_EQ(0 * sizeof(RVA), RVAAtIndex(string_file.string(), 0));
+ EXPECT_EQ(1 * sizeof(RVA), RVAAtIndex(string_file.string(), 1));
+ parent.Verify();
+ }
+
+ {
+ SCOPED_TRACE("parent-child only");
+ string_file.Reset();
+ TestRVAMinidumpWritable parent;
+ TestRVAMinidumpWritable child;
+ parent.SetRVA(&child);
+ parent.AddChild(&child);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+
+ ASSERT_EQ(2 * sizeof(RVA), string_file.string().size());
+ EXPECT_EQ(1 * sizeof(RVA), RVAAtIndex(string_file.string(), 0));
+ EXPECT_EQ(0 * sizeof(RVA), RVAAtIndex(string_file.string(), 1));
+ parent.Verify();
+ }
+
+ {
+ SCOPED_TRACE("parent-child circular");
+ string_file.Reset();
+ TestRVAMinidumpWritable parent;
+ TestRVAMinidumpWritable child;
+ parent.SetRVA(&child);
+ child.SetRVA(&parent);
+ parent.AddChild(&child);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+
+ ASSERT_EQ(2 * sizeof(RVA), string_file.string().size());
+ EXPECT_EQ(1 * sizeof(RVA), RVAAtIndex(string_file.string(), 0));
+ EXPECT_EQ(0 * sizeof(RVA), RVAAtIndex(string_file.string(), 1));
+ parent.Verify();
+ }
+
+ {
+ SCOPED_TRACE("grandchildren");
+ string_file.Reset();
+ TestRVAMinidumpWritable parent;
+ TestRVAMinidumpWritable child;
+ parent.SetRVA(&child);
+ parent.AddChild(&child);
+ TestRVAMinidumpWritable grandchild_0;
+ grandchild_0.SetRVA(&child);
+ child.AddChild(&grandchild_0);
+ TestRVAMinidumpWritable grandchild_1;
+ grandchild_1.SetRVA(&child);
+ child.AddChild(&grandchild_1);
+ TestRVAMinidumpWritable grandchild_2;
+ grandchild_2.SetRVA(&child);
+ child.AddChild(&grandchild_2);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+
+ ASSERT_EQ(5 * sizeof(RVA), string_file.string().size());
+ EXPECT_EQ(1 * sizeof(RVA), RVAAtIndex(string_file.string(), 0));
+ EXPECT_EQ(0 * sizeof(RVA), RVAAtIndex(string_file.string(), 1));
+ EXPECT_EQ(1 * sizeof(RVA), RVAAtIndex(string_file.string(), 2));
+ EXPECT_EQ(1 * sizeof(RVA), RVAAtIndex(string_file.string(), 3));
+ EXPECT_EQ(1 * sizeof(RVA), RVAAtIndex(string_file.string(), 4));
+ parent.Verify();
+ }
+}
+
+class TestLocationDescriptorMinidumpWritable final
+ : public BaseTestMinidumpWritable {
+ public:
+ TestLocationDescriptorMinidumpWritable()
+ : BaseTestMinidumpWritable(), location_descriptor_(), string_() {}
+
+ ~TestLocationDescriptorMinidumpWritable() {}
+
+ void SetLocationDescriptor(MinidumpWritable* other) {
+ other->RegisterLocationDescriptor(&location_descriptor_);
+ }
+
+ void SetString(const std::string& string) { string_ = string; }
+
+ protected:
+ size_t SizeOfObject() override {
+ EXPECT_GE(state(), kStateFrozen);
+ // NUL-terminate.
+ return sizeof(location_descriptor_) + string_.size() + 1;
+ }
+
+ bool WriteObject(FileWriterInterface* file_writer) override {
+ BaseTestMinidumpWritable::WriteObject(file_writer);
+ WritableIoVec iov;
+ iov.iov_base = &location_descriptor_;
+ iov.iov_len = sizeof(location_descriptor_);
+ std::vector<WritableIoVec> iovecs(1, iov);
+ // NUL-terminate.
+ iov.iov_base = &string_[0];
+ iov.iov_len = string_.size() + 1;
+ iovecs.push_back(iov);
+ EXPECT_TRUE(file_writer->WriteIoVec(&iovecs));
+ return true;
+ }
+
+ private:
+ MINIDUMP_LOCATION_DESCRIPTOR location_descriptor_;
+ std::string string_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestLocationDescriptorMinidumpWritable);
+};
+
+struct LocationDescriptorAndData {
+ MINIDUMP_LOCATION_DESCRIPTOR location_descriptor;
+ char string[1];
+};
+
+const LocationDescriptorAndData* LDDAtIndex(const std::string& string,
+ size_t index) {
+ return reinterpret_cast<const LocationDescriptorAndData*>(&string[index]);
+}
+
+TEST(MinidumpWritable, LocationDescriptor) {
+ StringFile string_file;
+
+ {
+ SCOPED_TRACE("unset");
+ string_file.Reset();
+ TestLocationDescriptorMinidumpWritable location_descriptor_writable;
+ EXPECT_TRUE(location_descriptor_writable.WriteEverything(&string_file));
+
+ ASSERT_EQ(9u, string_file.string().size());
+ const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0);
+ EXPECT_EQ(0u, ldd->location_descriptor.DataSize);
+ EXPECT_EQ(0u, ldd->location_descriptor.Rva);
+ location_descriptor_writable.Verify();
+ }
+
+ {
+ SCOPED_TRACE("self");
+ string_file.Reset();
+ TestLocationDescriptorMinidumpWritable location_descriptor_writable;
+ location_descriptor_writable.SetLocationDescriptor(
+ &location_descriptor_writable);
+ EXPECT_TRUE(location_descriptor_writable.WriteEverything(&string_file));
+
+ ASSERT_EQ(9u, string_file.string().size());
+ const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0);
+ EXPECT_EQ(9u, ldd->location_descriptor.DataSize);
+ EXPECT_EQ(0u, ldd->location_descriptor.Rva);
+ location_descriptor_writable.Verify();
+ }
+
+ {
+ SCOPED_TRACE("self with data");
+ string_file.Reset();
+ TestLocationDescriptorMinidumpWritable location_descriptor_writable;
+ location_descriptor_writable.SetLocationDescriptor(
+ &location_descriptor_writable);
+ location_descriptor_writable.SetString("zz");
+ EXPECT_TRUE(location_descriptor_writable.WriteEverything(&string_file));
+
+ ASSERT_EQ(11u, string_file.string().size());
+ const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0);
+ EXPECT_EQ(11u, ldd->location_descriptor.DataSize);
+ EXPECT_EQ(0u, ldd->location_descriptor.Rva);
+ EXPECT_STREQ("zz", ldd->string);
+ location_descriptor_writable.Verify();
+ }
+
+ {
+ SCOPED_TRACE("parent-child self");
+ string_file.Reset();
+ TestLocationDescriptorMinidumpWritable parent;
+ parent.SetLocationDescriptor(&parent);
+ parent.SetString("yy");
+ TestLocationDescriptorMinidumpWritable child;
+ child.SetLocationDescriptor(&child);
+ child.SetString("x");
+ parent.AddChild(&child);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+
+ ASSERT_EQ(22u, string_file.string().size());
+ const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0);
+ EXPECT_EQ(11u, ldd->location_descriptor.DataSize);
+ EXPECT_EQ(0u, ldd->location_descriptor.Rva);
+ EXPECT_STREQ("yy", ldd->string);
+ ldd = LDDAtIndex(string_file.string(), 12);
+ EXPECT_EQ(10u, ldd->location_descriptor.DataSize);
+ EXPECT_EQ(12u, ldd->location_descriptor.Rva);
+ EXPECT_STREQ("x", ldd->string);
+ parent.Verify();
+ }
+
+ {
+ SCOPED_TRACE("parent-child only");
+ string_file.Reset();
+ TestLocationDescriptorMinidumpWritable parent;
+ TestLocationDescriptorMinidumpWritable child;
+ parent.SetLocationDescriptor(&child);
+ parent.SetString("www");
+ child.SetString("vv");
+ parent.AddChild(&child);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+
+ ASSERT_EQ(23u, string_file.string().size());
+ const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0);
+ EXPECT_EQ(11u, ldd->location_descriptor.DataSize);
+ EXPECT_EQ(12u, ldd->location_descriptor.Rva);
+ EXPECT_STREQ("www", ldd->string);
+ ldd = LDDAtIndex(string_file.string(), 12);
+ EXPECT_EQ(0u, ldd->location_descriptor.DataSize);
+ EXPECT_EQ(0u, ldd->location_descriptor.Rva);
+ EXPECT_STREQ("vv", ldd->string);
+ parent.Verify();
+ }
+
+ {
+ SCOPED_TRACE("parent-child circular");
+ string_file.Reset();
+ TestLocationDescriptorMinidumpWritable parent;
+ TestLocationDescriptorMinidumpWritable child;
+ parent.SetLocationDescriptor(&child);
+ parent.SetString("uuuu");
+ child.SetLocationDescriptor(&parent);
+ child.SetString("tttt");
+ parent.AddChild(&child);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+
+ ASSERT_EQ(29u, string_file.string().size());
+ const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0);
+ EXPECT_EQ(13u, ldd->location_descriptor.DataSize);
+ EXPECT_EQ(16u, ldd->location_descriptor.Rva);
+ EXPECT_STREQ("uuuu", ldd->string);
+ ldd = LDDAtIndex(string_file.string(), 16);
+ EXPECT_EQ(13u, ldd->location_descriptor.DataSize);
+ EXPECT_EQ(0u, ldd->location_descriptor.Rva);
+ EXPECT_STREQ("tttt", ldd->string);
+ parent.Verify();
+ }
+
+ {
+ SCOPED_TRACE("grandchildren");
+ string_file.Reset();
+ TestLocationDescriptorMinidumpWritable parent;
+ TestLocationDescriptorMinidumpWritable child;
+ parent.SetLocationDescriptor(&child);
+ parent.SetString("s");
+ parent.AddChild(&child);
+ child.SetString("r");
+ TestLocationDescriptorMinidumpWritable grandchild_0;
+ grandchild_0.SetLocationDescriptor(&child);
+ grandchild_0.SetString("q");
+ child.AddChild(&grandchild_0);
+ TestLocationDescriptorMinidumpWritable grandchild_1;
+ grandchild_1.SetLocationDescriptor(&child);
+ grandchild_1.SetString("p");
+ child.AddChild(&grandchild_1);
+ TestLocationDescriptorMinidumpWritable grandchild_2;
+ grandchild_2.SetLocationDescriptor(&child);
+ grandchild_2.SetString("o");
+ child.AddChild(&grandchild_2);
+ EXPECT_TRUE(parent.WriteEverything(&string_file));
+
+ ASSERT_EQ(58u, string_file.string().size());
+ const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0);
+ EXPECT_EQ(10u, ldd->location_descriptor.DataSize);
+ EXPECT_EQ(12u, ldd->location_descriptor.Rva);
+ EXPECT_STREQ("s", ldd->string);
+ ldd = LDDAtIndex(string_file.string(), 12);
+ EXPECT_EQ(0u, ldd->location_descriptor.DataSize);
+ EXPECT_EQ(0u, ldd->location_descriptor.Rva);
+ EXPECT_STREQ("r", ldd->string);
+ ldd = LDDAtIndex(string_file.string(), 24);
+ EXPECT_EQ(10u, ldd->location_descriptor.DataSize);
+ EXPECT_EQ(12u, ldd->location_descriptor.Rva);
+ EXPECT_STREQ("q", ldd->string);
+ ldd = LDDAtIndex(string_file.string(), 36);
+ EXPECT_EQ(10u, ldd->location_descriptor.DataSize);
+ EXPECT_EQ(12u, ldd->location_descriptor.Rva);
+ EXPECT_STREQ("p", ldd->string);
+ ldd = LDDAtIndex(string_file.string(), 48);
+ EXPECT_EQ(10u, ldd->location_descriptor.DataSize);
+ EXPECT_EQ(12u, ldd->location_descriptor.Rva);
+ EXPECT_STREQ("o", ldd->string);
+ parent.Verify();
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_writer_util.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_writer_util.cc
new file mode 100644
index 00000000000..f8488b4dec9
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_writer_util.cc
@@ -0,0 +1,61 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "minidump/minidump_writer_util.h"
+
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "util/stdlib/strlcpy.h"
+
+namespace crashpad {
+namespace internal {
+
+// static
+void MinidumpWriterUtil::AssignTimeT(uint32_t* destination, time_t source) {
+ if (!base::IsValueInRangeForNumericType<uint32_t>(source)) {
+ LOG(WARNING) << "timestamp " << source << " out of range";
+ }
+
+ *destination = static_cast<uint32_t>(source);
+}
+
+// static
+base::string16 MinidumpWriterUtil::ConvertUTF8ToUTF16(const std::string& utf8) {
+ base::string16 utf16;
+ if (!base::UTF8ToUTF16(utf8.data(), utf8.length(), &utf16)) {
+ LOG(WARNING) << "string " << utf8
+ << " cannot be converted to UTF-16 losslessly";
+ }
+ return utf16;
+}
+
+// static
+void MinidumpWriterUtil::AssignUTF8ToUTF16(base::char16* destination,
+ size_t destination_size,
+ const std::string& source) {
+ base::string16 source_utf16 = ConvertUTF8ToUTF16(source);
+ if (source_utf16.size() > destination_size - 1) {
+ LOG(WARNING) << "string " << source << " UTF-16 length "
+ << source_utf16.size()
+ << " will be truncated to UTF-16 length "
+ << destination_size - 1;
+ }
+
+ source_utf16.resize(destination_size - 1);
+ c16lcpy(destination, source_utf16.c_str(), destination_size);
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_writer_util.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_writer_util.h
new file mode 100644
index 00000000000..3b2ab5e7bd8
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_writer_util.h
@@ -0,0 +1,90 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_MINIDUMP_MINIDUMP_WRITER_UTIL_H_
+#define CRASHPAD_MINIDUMP_MINIDUMP_WRITER_UTIL_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/strings/string16.h"
+
+namespace crashpad {
+namespace internal {
+
+//! \brief A collection of utility functions used by the MinidumpWritable family
+//! of classes.
+class MinidumpWriterUtil final {
+ public:
+ //! \brief Assigns a `time_t` value, logging a warning if the result overflows
+ //! the destination buffer and will be truncated.
+ //!
+ //! \param[out] destination A pointer to the variable to be assigned to.
+ //! \param[in] source The value to assign.
+ //!
+ //! The minidump format uses `uint32_t` for many timestamp values, but
+ //! `time_t` may be wider than this. These year 2038 bugs are a limitation of
+ //! the minidump format. An out-of-range error will be noted with a warning,
+ //! but is not considered fatal. \a source will be truncated and assigned to
+ //! \a destination in this case.
+ //!
+ //! For `time_t` values with nonfatal overflow semantics, this function is
+ //! used in preference to AssignIfInRange(), which fails without performing an
+ //! assignment when an out-of-range condition is detected.
+ static void AssignTimeT(uint32_t* destination, time_t source);
+
+ //! \brief Converts a UTF-8 string to UTF-16 and returns it. If the string
+ //! cannot be converted losslessly, indicating that the input is not
+ //! well-formed UTF-8, a warning is logged.
+ //!
+ //! \param[in] utf8 The UTF-8-encoded string to convert.
+ //!
+ //! \return The \a utf8 string, converted to UTF-16 encoding. If the
+ //! conversion is lossy, U+FFFD “replacement characters” will be
+ //! introduced.
+ static base::string16 ConvertUTF8ToUTF16(const std::string& utf8);
+
+ //! \brief Converts a UTF-8 string to UTF-16 and places it into a buffer of
+ //! fixed size, taking care to `NUL`-terminate the buffer and not to
+ //! overflow it. If the string will be truncated or if it cannot be
+ //! converted losslessly, a warning is logged.
+ //!
+ //! Any unused portion of the \a destination buffer that is not written to by
+ //! the converted string will be overwritten with `NUL` UTF-16 code units,
+ //! thus, this function always writes \a destination_size `char16` units.
+ //!
+ //! If the conversion is lossy, U+FFFD “replacement characters” will be
+ //! introduced.
+ //!
+ //! \param[out] destination A pointer to the destination buffer, where the
+ //! UTF-16-encoded string will be written.
+ //! \param[in] destination_size The size of \a destination in `char16` units,
+ //! including space used by a `NUL` terminator.
+ //! \param[in] source The UTF-8-encoded input string.
+ static void AssignUTF8ToUTF16(base::char16* destination,
+ size_t destination_size,
+ const std::string& source);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(MinidumpWriterUtil);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_MINIDUMP_MINIDUMP_WRITER_UTIL_H_
diff --git a/chromium/third_party/crashpad/crashpad/package.h b/chromium/third_party/crashpad/crashpad/package.h
new file mode 100644
index 00000000000..2c6f0f82c8c
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/package.h
@@ -0,0 +1,29 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_PACKAGE_H_
+#define CRASHPAD_PACKAGE_H_
+
+#define PACKAGE_BUGREPORT "https://code.google.com/p/crashpad/issues/entry"
+#define PACKAGE_COPYRIGHT \
+ "Copyright " PACKAGE_COPYRIGHT_YEAR " " PACKAGE_COPYRIGHT_OWNER
+#define PACKAGE_COPYRIGHT_OWNER "The Crashpad Authors"
+#define PACKAGE_COPYRIGHT_YEAR "2015"
+#define PACKAGE_NAME "Crashpad"
+#define PACKAGE_STRING PACKAGE_NAME " " PACKAGE_VERSION
+#define PACKAGE_TARNAME "crashpad"
+#define PACKAGE_VERSION "0.7.0"
+#define PACKAGE_URL "https://crashpad.googlecode.com/"
+
+#endif // CRASHPAD_PACKAGE_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/cpu_architecture.h b/chromium/third_party/crashpad/crashpad/snapshot/cpu_architecture.h
new file mode 100644
index 00000000000..208e98f275f
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/cpu_architecture.h
@@ -0,0 +1,39 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_ARCHITECTURE_H_
+#define CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_ARCHITECTURE_H_
+
+namespace crashpad {
+
+//! \brief A system’s CPU architecture.
+//!
+//! This can be used to represent the CPU architecture of an entire system
+//! as in SystemSnapshot::CPUArchitecture(). It can also be used to represent
+//! the architecture of a CPUContext structure in its CPUContext::architecture
+//! field without reference to external data.
+enum CPUArchitecture {
+ //! \brief The CPU architecture is unknown.
+ kCPUArchitectureUnknown = 0,
+
+ //! \brief 32-bit x86.
+ kCPUArchitectureX86,
+
+ //! \brief x86_64.
+ kCPUArchitectureX86_64,
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_ARCHITECTURE_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/cpu_context.cc b/chromium/third_party/crashpad/crashpad/snapshot/cpu_context.cc
new file mode 100644
index 00000000000..2f7ab976b26
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/cpu_context.cc
@@ -0,0 +1,100 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/cpu_context.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace crashpad {
+
+// static
+uint16_t CPUContextX86::FxsaveToFsaveTagWord(
+ uint16_t fsw,
+ uint8_t fxsave_tag,
+ const CPUContextX86::X87OrMMXRegister st_mm[8]) {
+ enum {
+ kX87TagValid = 0,
+ kX87TagZero,
+ kX87TagSpecial,
+ kX87TagEmpty,
+ };
+
+ // The x87 tag word (in both abridged and full form) identifies physical
+ // registers, but |st_mm| is arranged in logical stack order. In order to map
+ // physical tag word bits to the logical stack registers they correspond to,
+ // the “stack top” value from the x87 status word is necessary.
+ int stack_top = (fsw >> 11) & 0x7;
+
+ uint16_t fsave_tag = 0;
+ for (int physical_index = 0; physical_index < 8; ++physical_index) {
+ bool fxsave_bit = fxsave_tag & (1 << physical_index);
+ uint8_t fsave_bits;
+
+ if (fxsave_bit) {
+ int st_index = (physical_index + 8 - stack_top) % 8;
+ const CPUContextX86::X87Register& st = st_mm[st_index].st;
+
+ uint32_t exponent = ((st[9] & 0x7f) << 8) | st[8];
+ if (exponent == 0x7fff) {
+ // Infinity, NaN, pseudo-infinity, or pseudo-NaN. If it was important to
+ // distinguish between these, the J bit and the M bit (the most
+ // significant bit of |fraction|) could be consulted.
+ fsave_bits = kX87TagSpecial;
+ } else {
+ // The integer bit the “J bit”.
+ bool integer_bit = st[7] & 0x80;
+ if (exponent == 0) {
+ uint64_t fraction = ((implicit_cast<uint64_t>(st[7]) & 0x7f) << 56) |
+ (implicit_cast<uint64_t>(st[6]) << 48) |
+ (implicit_cast<uint64_t>(st[5]) << 40) |
+ (implicit_cast<uint64_t>(st[4]) << 32) |
+ (implicit_cast<uint32_t>(st[3]) << 24) |
+ (st[2] << 16) | (st[1] << 8) | st[0];
+ if (!integer_bit && fraction == 0) {
+ fsave_bits = kX87TagZero;
+ } else {
+ // Denormal (if the J bit is clear) or pseudo-denormal.
+ fsave_bits = kX87TagSpecial;
+ }
+ } else if (integer_bit) {
+ fsave_bits = kX87TagValid;
+ } else {
+ // Unnormal.
+ fsave_bits = kX87TagSpecial;
+ }
+ }
+ } else {
+ fsave_bits = kX87TagEmpty;
+ }
+
+ fsave_tag |= (fsave_bits << (physical_index * 2));
+ }
+
+ return fsave_tag;
+}
+
+uint64_t CPUContext::InstructionPointer() const {
+ switch (architecture) {
+ case kCPUArchitectureX86:
+ return x86->eip;
+ case kCPUArchitectureX86_64:
+ return x86_64->rip;
+ default:
+ NOTREACHED();
+ return ~0ull;
+ }
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/cpu_context.h b/chromium/third_party/crashpad/crashpad/snapshot/cpu_context.h
new file mode 100644
index 00000000000..bfef4d09df0
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/cpu_context.h
@@ -0,0 +1,219 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_CONTEXT_H_
+#define CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_CONTEXT_H_
+
+#include <stdint.h>
+
+#include "snapshot/cpu_architecture.h"
+
+namespace crashpad {
+
+//! \brief A context structure carrying 32-bit x86 CPU state.
+struct CPUContextX86 {
+ using X87Register = uint8_t[10];
+
+ union X87OrMMXRegister {
+ struct {
+ X87Register st;
+ uint8_t st_reserved[6];
+ };
+ struct {
+ uint8_t mm_value[8];
+ uint8_t mm_reserved[8];
+ };
+ };
+
+ using XMMRegister = uint8_t[16];
+
+ struct Fxsave {
+ uint16_t fcw; // FPU control word
+ uint16_t fsw; // FPU status word
+ uint8_t ftw; // abridged FPU tag word
+ uint8_t reserved_1;
+ uint16_t fop; // FPU opcode
+ uint32_t fpu_ip; // FPU instruction pointer offset
+ uint16_t fpu_cs; // FPU instruction pointer segment selector
+ uint16_t reserved_2;
+ uint32_t fpu_dp; // FPU data pointer offset
+ uint16_t fpu_ds; // FPU data pointer segment selector
+ uint16_t reserved_3;
+ uint32_t mxcsr; // multimedia extensions status and control register
+ uint32_t mxcsr_mask; // valid bits in mxcsr
+ X87OrMMXRegister st_mm[8];
+ XMMRegister xmm[8];
+ uint8_t reserved_4[176];
+ uint8_t available[48];
+ };
+
+ //! \brief Converts x87 floating-point tag words from `fxsave` (abridged,
+ //! 8-bit) to `fsave` (full, 16-bit) form.
+ //!
+ //! `fxsave` stores the x87 floating-point tag word in abridged 8-bit form,
+ //! and `fsave` stores it in full 16-bit form. Some users, notably
+ //! MinidumpContextX86::float_save::tag_word, require the full 16-bit form,
+ //! where most other contemporary code uses `fxsave` and thus the abridged
+ //! 8-bit form found in CPUContextX86::Fxsave::ftw.
+ //!
+ //! This function converts an abridged tag word to the full version by using
+ //! the abridged tag word and the contents of the registers it describes. See
+ //! Intel Software Developer’s Manual, Volume 2A: Instruction Set Reference
+ //! A-M (253666-052), 3.2 “FXSAVE”, specifically, the notes on the abridged
+ //! FTW and recreating the FSAVE format, and AMD Architecture Programmer’s
+ //! Manual, Volume 2: System Programming (24593-3.24), “FXSAVE Format for x87
+ //! Tag Word”.
+ //!
+ //! \param[in] fsw The FPU status word, used to map logical \a st_mm registers
+ //! to their physical counterparts. This can be taken from
+ //! CPUContextX86::Fxsave::fsw.
+ //! \param[in] fxsave_tag The abridged FPU tag word. This can be taken from
+ //! CPUContextX86::Fxsave::ftw.
+ //! \param[in] st_mm The floating-point registers in logical order. This can
+ //! be taken from CPUContextX86::Fxsave::st_mm.
+ //!
+ //! \return The full FPU tag word.
+ static uint16_t FxsaveToFsaveTagWord(
+ uint16_t fsw, uint8_t fxsave_tag, const X87OrMMXRegister st_mm[8]);
+
+ // Integer registers.
+ uint32_t eax;
+ uint32_t ebx;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t edi; // destination index
+ uint32_t esi; // source index
+ uint32_t ebp; // base pointer
+ uint32_t esp; // stack pointer
+ uint32_t eip; // instruction pointer
+ uint32_t eflags;
+ uint16_t cs; // code segment selector
+ uint16_t ds; // data segment selector
+ uint16_t es; // extra segment selector
+ uint16_t fs;
+ uint16_t gs;
+ uint16_t ss; // stack segment selector
+
+ // Floating-point and vector registers.
+ Fxsave fxsave;
+
+ // Debug registers.
+ uint32_t dr0;
+ uint32_t dr1;
+ uint32_t dr2;
+ uint32_t dr3;
+ uint32_t dr4; // obsolete, normally an alias for dr6
+ uint32_t dr5; // obsolete, normally an alias for dr7
+ uint32_t dr6;
+ uint32_t dr7;
+};
+
+//! \brief A context structure carrying x86_64 CPU state.
+struct CPUContextX86_64 {
+ using X87Register = CPUContextX86::X87Register;
+ using X87OrMMXRegister = CPUContextX86::X87OrMMXRegister;
+ using XMMRegister = CPUContextX86::XMMRegister;
+
+ struct Fxsave {
+ uint16_t fcw; // FPU control word
+ uint16_t fsw; // FPU status word
+ uint8_t ftw; // abridged FPU tag word
+ uint8_t reserved_1;
+ uint16_t fop; // FPU opcode
+ union {
+ // The expression of these union members is determined by the use of
+ // fxsave/fxrstor or fxsave64/fxrstor64 (fxsaveq/fxrstorq). Mac OS X and
+ // Windows systems use the traditional fxsave/fxrstor structure.
+ struct {
+ // fxsave/fxrstor
+ uint32_t fpu_ip; // FPU instruction pointer offset
+ uint16_t fpu_cs; // FPU instruction pointer segment selector
+ uint16_t reserved_2;
+ uint32_t fpu_dp; // FPU data pointer offset
+ uint16_t fpu_ds; // FPU data pointer segment selector
+ uint16_t reserved_3;
+ };
+ struct {
+ // fxsave64/fxrstor64 (fxsaveq/fxrstorq)
+ uint64_t fpu_ip_64; // FPU instruction pointer
+ uint64_t fpu_dp_64; // FPU data pointer
+ };
+ };
+ uint32_t mxcsr; // multimedia extensions status and control register
+ uint32_t mxcsr_mask; // valid bits in mxcsr
+ X87OrMMXRegister st_mm[8];
+ XMMRegister xmm[16];
+ uint8_t reserved_4[48];
+ uint8_t available[48];
+ };
+
+ // Integer registers.
+ uint64_t rax;
+ uint64_t rbx;
+ uint64_t rcx;
+ uint64_t rdx;
+ uint64_t rdi; // destination index
+ uint64_t rsi; // source index
+ uint64_t rbp; // base pointer
+ uint64_t rsp; // stack pointer
+ uint64_t r8;
+ uint64_t r9;
+ uint64_t r10;
+ uint64_t r11;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+ uint64_t rip; // instruction pointer
+ uint64_t rflags;
+ uint16_t cs; // code segment selector
+ uint16_t fs;
+ uint16_t gs;
+
+ // Floating-point and vector registers.
+ Fxsave fxsave;
+
+ // Debug registers.
+ uint64_t dr0;
+ uint64_t dr1;
+ uint64_t dr2;
+ uint64_t dr3;
+ uint64_t dr4; // obsolete, normally an alias for dr6
+ uint64_t dr5; // obsolete, normally an alias for dr7
+ uint64_t dr6;
+ uint64_t dr7;
+};
+
+//! \brief A context structure capable of carrying the context of any supported
+//! CPU architecture.
+struct CPUContext {
+ //! \brief Returns the instruction pointer value from the context structure.
+ //!
+ //! This is a CPU architecture-independent method that is capable of
+ //! recovering the instruction pointer from any supported CPU architecture’s
+ //! context structure.
+ uint64_t InstructionPointer() const;
+
+ //! \brief The CPU architecture of a context structure. This field controls
+ //! the expression of the union.
+ CPUArchitecture architecture;
+ union {
+ CPUContextX86* x86;
+ CPUContextX86_64* x86_64;
+ };
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_CONTEXT_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/cpu_context_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/cpu_context_test.cc
new file mode 100644
index 00000000000..a5f0b6aab6e
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/cpu_context_test.cc
@@ -0,0 +1,166 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/cpu_context.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include "base/basictypes.h"
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+enum ExponentValue {
+ kExponentAllZero = 0,
+ kExponentAllOne,
+ kExponentNormal,
+};
+
+enum FractionValue {
+ kFractionAllZero = 0,
+ kFractionNormal,
+};
+
+//! \brief Initializes an x87 register to a known bit pattern.
+//!
+//! \param[out] st_mm The x87 register to initialize. The reserved portion of
+//! the register is always zeroed out.
+//! \param[in] exponent_value The bit pattern to use for the exponent. If this
+//! is kExponentAllZero, the sign bit will be set to `1`, and if this is
+//! kExponentAllOne, the sign bit will be set to `0`. This tests that the
+//! implementation doesn’t erroneously consider the sign bit to be part of
+//! the exponent. This may also be kExponentNormal, indicating that the
+//! exponent shall neither be all zeroes nor all ones.
+//! \param[in] j_bit The value to use for the “J bit” (“integer bit”).
+//! \param[in] fraction_value If kFractionAllZero, the fraction will be zeroed
+//! out. If kFractionNormal, the fraction will not be all zeroes.
+void SetX87Register(CPUContextX86::X87OrMMXRegister* st_mm,
+ ExponentValue exponent_value,
+ bool j_bit,
+ FractionValue fraction_value) {
+ switch (exponent_value) {
+ case kExponentAllZero:
+ st_mm->st[9] = 0x80;
+ st_mm->st[8] = 0;
+ break;
+ case kExponentAllOne:
+ st_mm->st[9] = 0x7f;
+ st_mm->st[8] = 0xff;
+ break;
+ case kExponentNormal:
+ st_mm->st[9] = 0x55;
+ st_mm->st[8] = 0x55;
+ break;
+ }
+
+ uint8_t fraction_pattern = fraction_value == kFractionAllZero ? 0 : 0x55;
+ memset(&st_mm->st[0], fraction_pattern, 8);
+
+ if (j_bit) {
+ st_mm->st[7] |= 0x80;
+ } else {
+ st_mm->st[7] &= ~0x80;
+ }
+
+ memset(st_mm->st_reserved, 0, sizeof(st_mm->st_reserved));
+}
+
+TEST(CPUContextX86, FxsaveToFsaveTagWord) {
+ // The fsave tag word uses bit pattern 00 for valid, 01 for zero, 10 for
+ // “special”, and 11 for empty. Like the fxsave tag word, it is arranged by
+ // physical register. The fxsave tag word determines whether a register is
+ // empty, and analysis of the x87 register content distinguishes between
+ // valid, zero, and special. In the initializations below, comments show
+ // whether a register is expected to be considered valid, zero, or special,
+ // except where the tag word is expected to indicate that it is empty. Each
+ // combination appears twice: once where the fxsave tag word indicates a
+ // nonempty register, and once again where it indicates an empty register.
+
+ uint16_t fsw = 0 << 11; // top = 0: logical 0-7 maps to physical 0-7
+ uint8_t fxsave_tag = 0x0f; // physical 4-7 (logical 4-7) empty
+ CPUContextX86::X87OrMMXRegister st_mm[8];
+ SetX87Register(&st_mm[0], kExponentNormal, false, kFractionNormal); // spec.
+ SetX87Register(&st_mm[1], kExponentNormal, true, kFractionNormal); // valid
+ SetX87Register(&st_mm[2], kExponentNormal, false, kFractionAllZero); // spec.
+ SetX87Register(&st_mm[3], kExponentNormal, true, kFractionAllZero); // valid
+ SetX87Register(&st_mm[4], kExponentNormal, false, kFractionNormal);
+ SetX87Register(&st_mm[5], kExponentNormal, true, kFractionNormal);
+ SetX87Register(&st_mm[6], kExponentNormal, false, kFractionAllZero);
+ SetX87Register(&st_mm[7], kExponentNormal, true, kFractionAllZero);
+ EXPECT_EQ(0xff22,
+ CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm));
+
+ fsw = 2 << 11; // top = 2: logical 0-7 maps to physical 2-7, 0-1
+ fxsave_tag = 0xf0; // physical 0-3 (logical 6-7, 0-1) empty
+ SetX87Register(&st_mm[0], kExponentAllZero, false, kFractionNormal);
+ SetX87Register(&st_mm[1], kExponentAllZero, true, kFractionNormal);
+ SetX87Register(&st_mm[2], kExponentAllZero, false, kFractionAllZero); // zero
+ SetX87Register(&st_mm[3], kExponentAllZero, true, kFractionAllZero); // spec.
+ SetX87Register(&st_mm[4], kExponentAllZero, false, kFractionNormal); // spec.
+ SetX87Register(&st_mm[5], kExponentAllZero, true, kFractionNormal); // spec.
+ SetX87Register(&st_mm[6], kExponentAllZero, false, kFractionAllZero);
+ SetX87Register(&st_mm[7], kExponentAllZero, true, kFractionAllZero);
+ EXPECT_EQ(0xa9ff,
+ CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm));
+
+ fsw = 5 << 11; // top = 5: logical 0-7 maps to physical 5-7, 0-4
+ fxsave_tag = 0x5a; // physical 0, 2, 5, and 7 (logical 5, 0, 2, and 3) empty
+ SetX87Register(&st_mm[0], kExponentAllOne, false, kFractionNormal);
+ SetX87Register(&st_mm[1], kExponentAllOne, true, kFractionNormal); // spec.
+ SetX87Register(&st_mm[2], kExponentAllOne, false, kFractionAllZero);
+ SetX87Register(&st_mm[3], kExponentAllOne, true, kFractionAllZero);
+ SetX87Register(&st_mm[4], kExponentAllOne, false, kFractionNormal); // spec.
+ SetX87Register(&st_mm[5], kExponentAllOne, true, kFractionNormal);
+ SetX87Register(&st_mm[6], kExponentAllOne, false, kFractionAllZero); // spec.
+ SetX87Register(&st_mm[7], kExponentAllOne, true, kFractionAllZero); // spec.
+ EXPECT_EQ(0xeebb,
+ CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm));
+
+ // This set set is just a mix of all of the possible tag types in a single
+ // register file.
+ fsw = 1 << 11; // top = 1: logical 0-7 maps to physical 1-7, 0
+ fxsave_tag = 0x1f; // physical 5-7 (logical 4-6) empty
+ SetX87Register(&st_mm[0], kExponentNormal, true, kFractionAllZero); // valid
+ SetX87Register(&st_mm[1], kExponentAllZero, false, kFractionAllZero); // zero
+ SetX87Register(&st_mm[2], kExponentAllOne, true, kFractionAllZero); // spec.
+ SetX87Register(&st_mm[3], kExponentAllOne, true, kFractionNormal); // spec.
+ SetX87Register(&st_mm[4], kExponentAllZero, false, kFractionAllZero);
+ SetX87Register(&st_mm[5], kExponentAllZero, false, kFractionAllZero);
+ SetX87Register(&st_mm[6], kExponentAllZero, false, kFractionAllZero);
+ SetX87Register(&st_mm[7], kExponentNormal, true, kFractionNormal); // valid
+ EXPECT_EQ(0xfe90,
+ CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm));
+
+ // In this set, everything is valid.
+ fsw = 0 << 11; // top = 0: logical 0-7 maps to physical 0-7
+ fxsave_tag = 0xff; // nothing empty
+ for (size_t index = 0; index < arraysize(st_mm); ++index) {
+ SetX87Register(&st_mm[index], kExponentNormal, true, kFractionAllZero);
+ }
+ EXPECT_EQ(0, CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm));
+
+ // In this set, everything is empty. The registers shouldn’t be consulted at
+ // all, so they’re left alone from the previous set.
+ fsw = 0 << 11; // top = 0: logical 0-7 maps to physical 0-7
+ fxsave_tag = 0; // everything empty
+ EXPECT_EQ(0xffff,
+ CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm));
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options.cc b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options.cc
new file mode 100644
index 00000000000..e19b021e7d1
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options.cc
@@ -0,0 +1,44 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/crashpad_info_client_options.h"
+
+#include "base/logging.h"
+#include "client/crashpad_info.h"
+
+namespace crashpad {
+
+// static
+TriState CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
+ uint8_t crashpad_info_tri_state) {
+ switch (crashpad_info_tri_state) {
+ case static_cast<uint8_t>(TriState::kUnset):
+ return TriState::kUnset;
+ case static_cast<uint8_t>(TriState::kEnabled):
+ return TriState::kEnabled;
+ case static_cast<uint8_t>(TriState::kDisabled):
+ return TriState::kDisabled;
+ default:
+ LOG(WARNING) << "unknown TriState "
+ << static_cast<int>(crashpad_info_tri_state);
+ return TriState::kUnset;
+ }
+}
+
+CrashpadInfoClientOptions::CrashpadInfoClientOptions()
+ : crashpad_handler_behavior(TriState::kUnset),
+ system_crash_reporter_forwarding(TriState::kUnset) {
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options.h b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options.h
new file mode 100644
index 00000000000..2d6c35eab2e
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options.h
@@ -0,0 +1,64 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_CRASHPAD_INFO_CLIENT_OPTIONS_H_
+#define CRASHPAD_SNAPSHOT_CRASHPAD_INFO_CLIENT_OPTIONS_H_
+
+#include "util/misc/tri_state.h"
+
+namespace crashpad {
+
+//! \brief Options represented in a client’s CrashpadInfo structure.
+//!
+//! The CrashpadInfo structure is not suitable to expose client options
+//! in a generic way at the snapshot level. This structure duplicates
+//! option-related fields from the client structure for general use within the
+//! snapshot layer and by users of this layer.
+//!
+//! For objects of this type corresponding to a module, option values are taken
+//! from the module’s CrashpadInfo structure directly. If the module has no such
+//! such structure, option values appear unset.
+//!
+//! For objects of this type corresponding to an entire process, option values
+//! are taken from the CrashpadInfo structures of modules within the process.
+//! The first module found with a set value (enabled or disabled) will provide
+//! an option value for the process. Different modules may provide values for
+//! different options. If no module in the process sets a value for an option,
+//! the option will appear unset for the process. If no module in the process
+//! has a CrashpadInfo structure, all option values will appear unset.
+struct CrashpadInfoClientOptions {
+ public:
+ //! \brief Converts `uint8_t` value to a TriState value.
+ //!
+ //! The process_types layer exposes TriState as a `uint8_t` rather than an
+ //! enum type. This function converts these values into the equivalent enum
+ //! values used in the snapshot layer.
+ //!
+ //! \return The TriState equivalent of \a crashpad_info_tri_state, if it is a
+ //! valid TriState value. Otherwise, logs a warning and returns
+ //! TriState::kUnset.
+ static TriState TriStateFromCrashpadInfo(uint8_t crashpad_info_tri_state);
+
+ CrashpadInfoClientOptions();
+
+ //! \sa CrashpadInfo::set_crashpad_handler_behavior()
+ TriState crashpad_handler_behavior;
+
+ //! \sa CrashpadInfo::set_system_crash_reporter_forwarding()
+ TriState system_crash_reporter_forwarding;
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_CRASHPAD_INFO_CLIENT_OPTIONS_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options_test.cc
new file mode 100644
index 00000000000..acdc3257c58
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options_test.cc
@@ -0,0 +1,256 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/crashpad_info_client_options.h"
+
+#include "base/files/file_path.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "client/crashpad_info.h"
+#include "gtest/gtest.h"
+#include "test/errors.h"
+#include "test/paths.h"
+
+#if defined(OS_MACOSX)
+#include <dlfcn.h>
+#include "snapshot/mac/process_snapshot_mac.h"
+#elif defined(OS_WIN)
+#include <windows.h>
+#include "snapshot/win/process_snapshot_win.h"
+#endif
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(CrashpadInfoClientOptions, TriStateFromCrashpadInfo) {
+ EXPECT_EQ(TriState::kUnset,
+ CrashpadInfoClientOptions::TriStateFromCrashpadInfo(0));
+ EXPECT_EQ(TriState::kEnabled,
+ CrashpadInfoClientOptions::TriStateFromCrashpadInfo(1));
+ EXPECT_EQ(TriState::kDisabled,
+ CrashpadInfoClientOptions::TriStateFromCrashpadInfo(2));
+
+ // These will produce log messages but should result in kUnset being returned.
+ EXPECT_EQ(TriState::kUnset,
+ CrashpadInfoClientOptions::TriStateFromCrashpadInfo(3));
+ EXPECT_EQ(TriState::kUnset,
+ CrashpadInfoClientOptions::TriStateFromCrashpadInfo(4));
+ EXPECT_EQ(TriState::kUnset,
+ CrashpadInfoClientOptions::TriStateFromCrashpadInfo(0xff));
+}
+
+class ScopedUnsetCrashpadInfoOptions {
+ public:
+ explicit ScopedUnsetCrashpadInfoOptions(CrashpadInfo* crashpad_info)
+ : crashpad_info_(crashpad_info) {
+ }
+
+ ~ScopedUnsetCrashpadInfoOptions() {
+ crashpad_info_->set_crashpad_handler_behavior(TriState::kUnset);
+ crashpad_info_->set_system_crash_reporter_forwarding(TriState::kUnset);
+ }
+
+ private:
+ CrashpadInfo* crashpad_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedUnsetCrashpadInfoOptions);
+};
+
+TEST(CrashpadInfoClientOptions, OneModule) {
+ // Make sure that the initial state has all values unset.
+#if defined(OS_MACOSX)
+ ProcessSnapshotMac process_snapshot;
+ ASSERT_TRUE(process_snapshot.Initialize(mach_task_self()));
+#elif defined(OS_WIN)
+ ProcessSnapshotWin process_snapshot;
+ ASSERT_TRUE(process_snapshot.Initialize(GetCurrentProcess()));
+#else
+#error Port.
+#endif // OS_MACOSX
+
+ CrashpadInfoClientOptions options;
+ process_snapshot.GetCrashpadOptions(&options);
+
+ EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior);
+ EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding);
+
+ CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo();
+ ASSERT_TRUE(crashpad_info);
+
+ {
+ ScopedUnsetCrashpadInfoOptions unset(crashpad_info);
+
+ crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled);
+
+ process_snapshot.GetCrashpadOptions(&options);
+ EXPECT_EQ(TriState::kEnabled, options.crashpad_handler_behavior);
+ EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding);
+ }
+
+ {
+ ScopedUnsetCrashpadInfoOptions unset(crashpad_info);
+
+ crashpad_info->set_system_crash_reporter_forwarding(TriState::kDisabled);
+
+ process_snapshot.GetCrashpadOptions(&options);
+ EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior);
+ EXPECT_EQ(TriState::kDisabled, options.system_crash_reporter_forwarding);
+ }
+}
+
+#if defined(OS_POSIX)
+using DlHandle = void*;
+#elif defined(OS_WIN)
+using DlHandle = HMODULE;
+#endif // OS_POSIX
+
+class ScopedDlHandle {
+ public:
+ explicit ScopedDlHandle(DlHandle dl_handle)
+ : dl_handle_(dl_handle) {
+ }
+
+ ~ScopedDlHandle() {
+ if (dl_handle_) {
+#if defined(OS_POSIX)
+ if (dlclose(dl_handle_) != 0) {
+ LOG(ERROR) << "dlclose: " << dlerror();
+ }
+#elif defined(OS_WIN)
+ if (!FreeLibrary(dl_handle_))
+ PLOG(ERROR) << "FreeLibrary";
+#endif // OS_POSIX
+ }
+ }
+
+ bool valid() const { return dl_handle_ != nullptr; }
+
+ template <typename T>
+ T LookUpSymbol(const char* symbol_name) {
+#if defined(OS_POSIX)
+ return reinterpret_cast<T>(dlsym(dl_handle_, symbol_name));
+#elif defined(OS_WIN)
+ return reinterpret_cast<T>(GetProcAddress(dl_handle_, symbol_name));
+#endif // OS_POSIX
+ }
+
+ private:
+ DlHandle dl_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedDlHandle);
+};
+
+TEST(CrashpadInfoClientOptions, TwoModules) {
+ // Open the module, which has its own CrashpadInfo structure.
+#if defined(OS_MACOSX)
+ const base::FilePath::StringType kDlExtension = FILE_PATH_LITERAL(".so");
+#elif defined(OS_WIN)
+ const base::FilePath::StringType kDlExtension = FILE_PATH_LITERAL(".dll");
+#endif
+ base::FilePath module_path = Paths::Executable().DirName().Append(
+ FILE_PATH_LITERAL("crashpad_snapshot_test_module") + kDlExtension);
+#if defined(OS_MACOSX)
+ ScopedDlHandle dl_handle(
+ dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL));
+ ASSERT_TRUE(dl_handle.valid()) << "dlopen " << module_path.value() << ": "
+ << dlerror();
+#elif defined(OS_WIN)
+ ScopedDlHandle dl_handle(LoadLibrary(module_path.value().c_str()));
+ ASSERT_TRUE(dl_handle.valid())
+ << "LoadLibrary " << base::UTF16ToUTF8(module_path.value()) << ": "
+ << ErrorMessage();
+#else
+#error Port.
+#endif // OS_MACOSX
+
+ // Get the function pointer from the module. This wraps GetCrashpadInfo(), but
+ // because it runs in the module, it returns the remote module’s CrashpadInfo
+ // structure.
+ CrashpadInfo* (*TestModule_GetCrashpadInfo)() =
+ dl_handle.LookUpSymbol<CrashpadInfo* (*)()>("TestModule_GetCrashpadInfo");
+ ASSERT_TRUE(TestModule_GetCrashpadInfo);
+
+ // Make sure that the initial state has all values unset.
+#if defined(OS_MACOSX)
+ ProcessSnapshotMac process_snapshot;
+ ASSERT_TRUE(process_snapshot.Initialize(mach_task_self()));
+#elif defined(OS_WIN)
+ ProcessSnapshotWin process_snapshot;
+ ASSERT_TRUE(process_snapshot.Initialize(GetCurrentProcess()));
+#else
+#error Port.
+#endif // OS_MACOSX
+
+ CrashpadInfoClientOptions options;
+ process_snapshot.GetCrashpadOptions(&options);
+
+ EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior);
+ EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding);
+
+ // Get both CrashpadInfo structures.
+ CrashpadInfo* local_crashpad_info = CrashpadInfo::GetCrashpadInfo();
+ ASSERT_TRUE(local_crashpad_info);
+
+ CrashpadInfo* remote_crashpad_info = TestModule_GetCrashpadInfo();
+ ASSERT_TRUE(remote_crashpad_info);
+
+ {
+ ScopedUnsetCrashpadInfoOptions unset_local(local_crashpad_info);
+ ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info);
+
+ // When only one module sets a value, it applies to the entire process.
+ remote_crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled);
+
+ process_snapshot.GetCrashpadOptions(&options);
+ EXPECT_EQ(TriState::kEnabled, options.crashpad_handler_behavior);
+ EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding);
+
+ // When more than one module sets a value, the first one in the module list
+ // applies to the process. The local module should appear before the remote
+ // module, because the local module loaded the remote module.
+ local_crashpad_info->set_crashpad_handler_behavior(TriState::kDisabled);
+
+ process_snapshot.GetCrashpadOptions(&options);
+ EXPECT_EQ(TriState::kDisabled, options.crashpad_handler_behavior);
+ EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding);
+ }
+
+ {
+ ScopedUnsetCrashpadInfoOptions unset_local(local_crashpad_info);
+ ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info);
+
+ // When only one module sets a value, it applies to the entire process.
+ remote_crashpad_info->set_system_crash_reporter_forwarding(
+ TriState::kDisabled);
+
+ process_snapshot.GetCrashpadOptions(&options);
+ EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior);
+ EXPECT_EQ(TriState::kDisabled, options.system_crash_reporter_forwarding);
+
+ // When more than one module sets a value, the first one in the module list
+ // applies to the process. The local module should appear before the remote
+ // module, because the local module loaded the remote module.
+ local_crashpad_info->set_system_crash_reporter_forwarding(
+ TriState::kEnabled);
+
+ process_snapshot.GetCrashpadOptions(&options);
+ EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior);
+ EXPECT_EQ(TriState::kEnabled, options.system_crash_reporter_forwarding);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options_test_module.cc b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options_test_module.cc
new file mode 100644
index 00000000000..357d35e6596
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options_test_module.cc
@@ -0,0 +1,47 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "build/build_config.h"
+#include "client/crashpad_info.h"
+
+#if defined(OS_POSIX)
+#define EXPORT __attribute__((visibility("default")))
+#elif defined(OS_WIN)
+#include <windows.h>
+#define EXPORT __declspec(dllexport)
+#endif // OS_POSIX
+
+extern "C" {
+
+// Returns the module’s CrashpadInfo structure. Assuming that this file is built
+// into a loadable_module with a distinct static copy of the Crashpad client
+// library from the copy built into the loader of this loadable_module, this
+// will return a different CrashpadInfo structure than the one that the loader
+// uses. Having an extra CrashpadInfo structure makes it possible to test
+// behaviors that are relevant in the presence of multiple Crashpad
+// client-enabled modules.
+//
+// This function is used by the CrashpadInfoClientOptions.TwoModules test in
+// crashpad_info_client_options_test.cc.
+EXPORT crashpad::CrashpadInfo* TestModule_GetCrashpadInfo() {
+ return crashpad::CrashpadInfo::GetCrashpadInfo();
+}
+
+} // extern "C"
+
+#if defined(OS_WIN)
+BOOL WINAPI DllMain(HINSTANCE hinstance, DWORD reason, LPVOID reserved) {
+ return TRUE;
+}
+#endif // OS_WIN
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/exception_snapshot.h b/chromium/third_party/crashpad/crashpad/snapshot/exception_snapshot.h
new file mode 100644
index 00000000000..f5ffd06a229
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/exception_snapshot.h
@@ -0,0 +1,100 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_EXCEPTION_SNAPSHOT_H_
+#define CRASHPAD_SNAPSHOT_EXCEPTION_SNAPSHOT_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+namespace crashpad {
+
+struct CPUContext;
+
+//! \brief An abstract interface to a snapshot representing an exception that a
+//! snapshot process sustained and triggered the snapshot being taken.
+class ExceptionSnapshot {
+ public:
+ virtual ~ExceptionSnapshot() {}
+
+ //! \brief Returns a CPUContext object corresponding to the exception thread’s
+ //! CPU context at the time of the exception.
+ //!
+ //! The caller does not take ownership of this object, it is scoped to the
+ //! lifetime of the ThreadSnapshot object that it was obtained from.
+ virtual const CPUContext* Context() const = 0;
+
+ //! \brief Returns the thread identifier of the thread that triggered the
+ //! exception.
+ //!
+ //! This value can be compared to ThreadSnapshot::ThreadID() to associate an
+ //! ExceptionSnapshot object with the ThreadSnapshot that contains a snapshot
+ //! of the thread that triggered the exception.
+ virtual uint64_t ThreadID() const = 0;
+
+ //! \brief Returns the top-level exception code identifying the exception.
+ //!
+ //! This is an operating system-specific value.
+ //!
+ //! For Mac OS X, this will be an \ref EXC_x "EXC_*" exception type, such as
+ //! `EXC_BAD_ACCESS`. `EXC_CRASH` will not appear here for exceptions
+ //! processed as `EXC_CRASH` when generated from another preceding exception:
+ //! the original exception code will appear instead. The exception type as it
+ //! was received will appear at index 0 of Codes().
+ virtual uint32_t Exception() const = 0;
+
+ //! \brief Returns the second-level exception code identifying the exception.
+ //!
+ //! This is an operating system-specific value.
+ //!
+ //! For Mac OS X, this will be the value of the exception code at index 0 as
+ //! received by a Mach exception handler, except:
+ //! * For `EXC_CRASH` exceptions generated from another preceding exception,
+ //! the original exception code will appear here, not the code as received
+ //! by the Mach exception handler.
+ //! * For `EXC_RESOURCE` and `EXC_GUARD` exceptions, the high 32 bits of the
+ //! exception code at index 0 will appear here.
+ //!
+ //! In all cases on Mac OS X, the full exception code at index 0 as it was
+ //! received will appear at index 1 of Codes().
+ virtual uint32_t ExceptionInfo() const = 0;
+
+ //! \brief Returns the address that triggered the exception.
+ //!
+ //! This may be the address that caused a fault on data access, or it may be
+ //! the instruction pointer that contained an offending instruction. For
+ //! exceptions where this value cannot be determined, it will be `0`.
+ //!
+ //! For Mac OS X, this will be the value of the exception code at index 1 as
+ //! received by a Mach exception handler.
+ virtual uint64_t ExceptionAddress() const = 0;
+
+ //! \brief Returns a series of operating system-specific exception codes.
+ //!
+ //! The precise interpretation of these codes is specific to the snapshot
+ //! operating system. These codes may provide a duplicate of information
+ //! available elsewhere, they may extend information available elsewhere, or
+ //! they may not be present at all. In this case, an empty vector will be
+ //! returned.
+ //!
+ //! For Mac OS X, this will be a vector containing the original exception type
+ //! and the values of `code[0]` and `code[1]` as received by a Mach exception
+ //! handler.
+ virtual const std::vector<uint64_t>& Codes() const = 0;
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_EXCEPTION_SNAPSHOT_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac.cc
new file mode 100644
index 00000000000..8dca246caec
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac.cc
@@ -0,0 +1,440 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/mac/cpu_context_mac.h"
+
+#include <string.h>
+
+#include "base/logging.h"
+
+namespace crashpad {
+
+#if defined(ARCH_CPU_X86_FAMILY)
+
+namespace {
+
+void InitializeCPUContextX86Thread(
+ CPUContextX86* context,
+ const x86_thread_state32_t* x86_thread_state32) {
+ context->eax = x86_thread_state32->__eax;
+ context->ebx = x86_thread_state32->__ebx;
+ context->ecx = x86_thread_state32->__ecx;
+ context->edx = x86_thread_state32->__edx;
+ context->edi = x86_thread_state32->__edi;
+ context->esi = x86_thread_state32->__esi;
+ context->ebp = x86_thread_state32->__ebp;
+ context->esp = x86_thread_state32->__esp;
+ context->eip = x86_thread_state32->__eip;
+ context->eflags = x86_thread_state32->__eflags;
+ context->cs = x86_thread_state32->__cs;
+ context->ds = x86_thread_state32->__ds;
+ context->es = x86_thread_state32->__es;
+ context->fs = x86_thread_state32->__fs;
+ context->gs = x86_thread_state32->__gs;
+ context->ss = x86_thread_state32->__ss;
+}
+
+void InitializeCPUContextX86Float(
+ CPUContextX86* context, const x86_float_state32_t* x86_float_state32) {
+ // This relies on both x86_float_state32_t and context->fxsave having
+ // identical (fxsave) layout.
+ static_assert(offsetof(x86_float_state32_t, __fpu_reserved1) -
+ offsetof(x86_float_state32_t, __fpu_fcw) ==
+ sizeof(context->fxsave),
+ "types must be equivalent");
+
+ memcpy(
+ &context->fxsave, &x86_float_state32->__fpu_fcw, sizeof(context->fxsave));
+}
+
+void InitializeCPUContextX86Debug(
+ CPUContextX86* context, const x86_debug_state32_t* x86_debug_state32) {
+ context->dr0 = x86_debug_state32->__dr0;
+ context->dr1 = x86_debug_state32->__dr1;
+ context->dr2 = x86_debug_state32->__dr2;
+ context->dr3 = x86_debug_state32->__dr3;
+ context->dr4 = x86_debug_state32->__dr4;
+ context->dr5 = x86_debug_state32->__dr5;
+ context->dr6 = x86_debug_state32->__dr6;
+ context->dr7 = x86_debug_state32->__dr7;
+}
+
+// Initializes |context| from the native thread state structure |state|, which
+// is interpreted according to |flavor|. |state_count| must be at least the
+// expected size for |flavor|. This handles the architecture-specific
+// x86_THREAD_STATE32, x86_FLOAT_STATE32, and x86_DEBUG_STATE32 flavors. It also
+// handles the universal x86_THREAD_STATE, x86_FLOAT_STATE, and x86_DEBUG_STATE
+// flavors provided that the associated structure carries 32-bit data of the
+// corresponding state type. |flavor| may be THREAD_STATE_NONE to avoid setting
+// any thread state in |context|. This returns the architecture-specific flavor
+// value for the thread state that was actually set, or THREAD_STATE_NONE if no
+// thread state was set.
+thread_state_flavor_t InitializeCPUContextX86Flavor(
+ CPUContextX86* context,
+ thread_state_flavor_t flavor,
+ ConstThreadState state,
+ mach_msg_type_number_t state_count) {
+ mach_msg_type_number_t expected_state_count;
+ switch (flavor) {
+ case x86_THREAD_STATE:
+ expected_state_count = x86_THREAD_STATE_COUNT;
+ break;
+ case x86_FLOAT_STATE:
+ expected_state_count = x86_FLOAT_STATE_COUNT;
+ break;
+ case x86_DEBUG_STATE:
+ expected_state_count = x86_DEBUG_STATE_COUNT;
+ break;
+ case x86_THREAD_STATE32:
+ expected_state_count = x86_THREAD_STATE32_COUNT;
+ break;
+ case x86_FLOAT_STATE32:
+ expected_state_count = x86_FLOAT_STATE32_COUNT;
+ break;
+ case x86_DEBUG_STATE32:
+ expected_state_count = x86_DEBUG_STATE32_COUNT;
+ break;
+ case THREAD_STATE_NONE:
+ expected_state_count = 0;
+ break;
+ default:
+ LOG(WARNING) << "unhandled flavor " << flavor;
+ return THREAD_STATE_NONE;
+ }
+
+ if (state_count < expected_state_count) {
+ LOG(WARNING) << "expected state_count " << expected_state_count
+ << " for flavor " << flavor << ", observed " << state_count;
+ return THREAD_STATE_NONE;
+ }
+
+ switch (flavor) {
+ case x86_THREAD_STATE: {
+ const x86_thread_state_t* x86_thread_state =
+ reinterpret_cast<const x86_thread_state_t*>(state);
+ if (x86_thread_state->tsh.flavor != x86_THREAD_STATE32) {
+ LOG(WARNING) << "expected flavor x86_THREAD_STATE32, observed "
+ << x86_thread_state->tsh.flavor;
+ return THREAD_STATE_NONE;
+ }
+ return InitializeCPUContextX86Flavor(
+ context,
+ x86_thread_state->tsh.flavor,
+ reinterpret_cast<ConstThreadState>(&x86_thread_state->uts.ts32),
+ x86_thread_state->tsh.count);
+ }
+
+ case x86_FLOAT_STATE: {
+ const x86_float_state_t* x86_float_state =
+ reinterpret_cast<const x86_float_state_t*>(state);
+ if (x86_float_state->fsh.flavor != x86_FLOAT_STATE32) {
+ LOG(WARNING) << "expected flavor x86_FLOAT_STATE32, observed "
+ << x86_float_state->fsh.flavor;
+ return THREAD_STATE_NONE;
+ }
+ return InitializeCPUContextX86Flavor(
+ context,
+ x86_float_state->fsh.flavor,
+ reinterpret_cast<ConstThreadState>(&x86_float_state->ufs.fs32),
+ x86_float_state->fsh.count);
+ }
+
+ case x86_DEBUG_STATE: {
+ const x86_debug_state_t* x86_debug_state =
+ reinterpret_cast<const x86_debug_state_t*>(state);
+ if (x86_debug_state->dsh.flavor != x86_DEBUG_STATE32) {
+ LOG(WARNING) << "expected flavor x86_DEBUG_STATE32, observed "
+ << x86_debug_state->dsh.flavor;
+ return THREAD_STATE_NONE;
+ }
+ return InitializeCPUContextX86Flavor(
+ context,
+ x86_debug_state->dsh.flavor,
+ reinterpret_cast<ConstThreadState>(&x86_debug_state->uds.ds32),
+ x86_debug_state->dsh.count);
+ }
+
+ case x86_THREAD_STATE32: {
+ const x86_thread_state32_t* x86_thread_state32 =
+ reinterpret_cast<const x86_thread_state32_t*>(state);
+ InitializeCPUContextX86Thread(context, x86_thread_state32);
+ return flavor;
+ }
+
+ case x86_FLOAT_STATE32: {
+ const x86_float_state32_t* x86_float_state32 =
+ reinterpret_cast<const x86_float_state32_t*>(state);
+ InitializeCPUContextX86Float(context, x86_float_state32);
+ return flavor;
+ }
+
+ case x86_DEBUG_STATE32: {
+ const x86_debug_state32_t* x86_debug_state32 =
+ reinterpret_cast<const x86_debug_state32_t*>(state);
+ InitializeCPUContextX86Debug(context, x86_debug_state32);
+ return flavor;
+ }
+
+ case THREAD_STATE_NONE: {
+ // This may happen without error when called without exception-style
+ // flavor data, or even from an exception handler when the exception
+ // behavior is EXCEPTION_DEFAULT.
+ return flavor;
+ }
+
+ default: {
+ NOTREACHED();
+ return THREAD_STATE_NONE;
+ }
+ }
+}
+
+void InitializeCPUContextX86_64Thread(
+ CPUContextX86_64* context, const x86_thread_state64_t* x86_thread_state64) {
+ context->rax = x86_thread_state64->__rax;
+ context->rbx = x86_thread_state64->__rbx;
+ context->rcx = x86_thread_state64->__rcx;
+ context->rdx = x86_thread_state64->__rdx;
+ context->rdi = x86_thread_state64->__rdi;
+ context->rsi = x86_thread_state64->__rsi;
+ context->rbp = x86_thread_state64->__rbp;
+ context->rsp = x86_thread_state64->__rsp;
+ context->r8 = x86_thread_state64->__r8;
+ context->r9 = x86_thread_state64->__r9;
+ context->r10 = x86_thread_state64->__r10;
+ context->r11 = x86_thread_state64->__r11;
+ context->r12 = x86_thread_state64->__r12;
+ context->r13 = x86_thread_state64->__r13;
+ context->r14 = x86_thread_state64->__r14;
+ context->r15 = x86_thread_state64->__r15;
+ context->rip = x86_thread_state64->__rip;
+ context->rflags = x86_thread_state64->__rflags;
+ context->cs = x86_thread_state64->__cs;
+ context->fs = x86_thread_state64->__fs;
+ context->gs = x86_thread_state64->__gs;
+}
+
+void InitializeCPUContextX86_64Float(
+ CPUContextX86_64* context, const x86_float_state64_t* x86_float_state64) {
+ // This relies on both x86_float_state64_t and context->fxsave having
+ // identical (fxsave) layout.
+ static_assert(offsetof(x86_float_state64_t, __fpu_reserved1) -
+ offsetof(x86_float_state64_t, __fpu_fcw) ==
+ sizeof(context->fxsave),
+ "types must be equivalent");
+
+ memcpy(&context->fxsave,
+ &x86_float_state64->__fpu_fcw,
+ sizeof(context->fxsave));
+}
+
+void InitializeCPUContextX86_64Debug(
+ CPUContextX86_64* context, const x86_debug_state64_t* x86_debug_state64) {
+ context->dr0 = x86_debug_state64->__dr0;
+ context->dr1 = x86_debug_state64->__dr1;
+ context->dr2 = x86_debug_state64->__dr2;
+ context->dr3 = x86_debug_state64->__dr3;
+ context->dr4 = x86_debug_state64->__dr4;
+ context->dr5 = x86_debug_state64->__dr5;
+ context->dr6 = x86_debug_state64->__dr6;
+ context->dr7 = x86_debug_state64->__dr7;
+}
+
+// Initializes |context| from the native thread state structure |state|, which
+// is interpreted according to |flavor|. |state_count| must be at least the
+// expected size for |flavor|. This handles the architecture-specific
+// x86_THREAD_STATE64, x86_FLOAT_STATE64, and x86_DEBUG_STATE64 flavors. It also
+// handles the universal x86_THREAD_STATE, x86_FLOAT_STATE, and x86_DEBUG_STATE
+// flavors provided that the associated structure carries 64-bit data of the
+// corresponding state type. |flavor| may be THREAD_STATE_NONE to avoid setting
+// any thread state in |context|. This returns the architecture-specific flavor
+// value for the thread state that was actually set, or THREAD_STATE_NONE if no
+// thread state was set.
+thread_state_flavor_t InitializeCPUContextX86_64Flavor(
+ CPUContextX86_64* context,
+ thread_state_flavor_t flavor,
+ ConstThreadState state,
+ mach_msg_type_number_t state_count) {
+ mach_msg_type_number_t expected_state_count;
+ switch (flavor) {
+ case x86_THREAD_STATE:
+ expected_state_count = x86_THREAD_STATE_COUNT;
+ break;
+ case x86_FLOAT_STATE:
+ expected_state_count = x86_FLOAT_STATE_COUNT;
+ break;
+ case x86_DEBUG_STATE:
+ expected_state_count = x86_DEBUG_STATE_COUNT;
+ break;
+ case x86_THREAD_STATE64:
+ expected_state_count = x86_THREAD_STATE64_COUNT;
+ break;
+ case x86_FLOAT_STATE64:
+ expected_state_count = x86_FLOAT_STATE64_COUNT;
+ break;
+ case x86_DEBUG_STATE64:
+ expected_state_count = x86_DEBUG_STATE64_COUNT;
+ break;
+ case THREAD_STATE_NONE:
+ expected_state_count = 0;
+ break;
+ default:
+ LOG(WARNING) << "unhandled flavor " << flavor;
+ return THREAD_STATE_NONE;
+ }
+
+ if (state_count < expected_state_count) {
+ LOG(WARNING) << "expected state_count " << expected_state_count
+ << " for flavor " << flavor << ", observed " << state_count;
+ return THREAD_STATE_NONE;
+ }
+
+ switch (flavor) {
+ case x86_THREAD_STATE: {
+ const x86_thread_state_t* x86_thread_state =
+ reinterpret_cast<const x86_thread_state_t*>(state);
+ if (x86_thread_state->tsh.flavor != x86_THREAD_STATE64) {
+ LOG(WARNING) << "expected flavor x86_THREAD_STATE64, observed "
+ << x86_thread_state->tsh.flavor;
+ return THREAD_STATE_NONE;
+ }
+ return InitializeCPUContextX86_64Flavor(
+ context,
+ x86_thread_state->tsh.flavor,
+ reinterpret_cast<ConstThreadState>(&x86_thread_state->uts.ts64),
+ x86_thread_state->tsh.count);
+ }
+
+ case x86_FLOAT_STATE: {
+ const x86_float_state_t* x86_float_state =
+ reinterpret_cast<const x86_float_state_t*>(state);
+ if (x86_float_state->fsh.flavor != x86_FLOAT_STATE64) {
+ LOG(WARNING) << "expected flavor x86_FLOAT_STATE64, observed "
+ << x86_float_state->fsh.flavor;
+ return THREAD_STATE_NONE;
+ }
+ return InitializeCPUContextX86_64Flavor(
+ context,
+ x86_float_state->fsh.flavor,
+ reinterpret_cast<ConstThreadState>(&x86_float_state->ufs.fs64),
+ x86_float_state->fsh.count);
+ }
+
+ case x86_DEBUG_STATE: {
+ const x86_debug_state_t* x86_debug_state =
+ reinterpret_cast<const x86_debug_state_t*>(state);
+ if (x86_debug_state->dsh.flavor != x86_DEBUG_STATE64) {
+ LOG(WARNING) << "expected flavor x86_DEBUG_STATE64, observed "
+ << x86_debug_state->dsh.flavor;
+ return THREAD_STATE_NONE;
+ }
+ return InitializeCPUContextX86_64Flavor(
+ context,
+ x86_debug_state->dsh.flavor,
+ reinterpret_cast<ConstThreadState>(&x86_debug_state->uds.ds64),
+ x86_debug_state->dsh.count);
+ }
+
+ case x86_THREAD_STATE64: {
+ const x86_thread_state64_t* x86_thread_state64 =
+ reinterpret_cast<const x86_thread_state64_t*>(state);
+ InitializeCPUContextX86_64Thread(context, x86_thread_state64);
+ return flavor;
+ }
+
+ case x86_FLOAT_STATE64: {
+ const x86_float_state64_t* x86_float_state64 =
+ reinterpret_cast<const x86_float_state64_t*>(state);
+ InitializeCPUContextX86_64Float(context, x86_float_state64);
+ return flavor;
+ }
+
+ case x86_DEBUG_STATE64: {
+ const x86_debug_state64_t* x86_debug_state64 =
+ reinterpret_cast<const x86_debug_state64_t*>(state);
+ InitializeCPUContextX86_64Debug(context, x86_debug_state64);
+ return flavor;
+ }
+
+ case THREAD_STATE_NONE: {
+ // This may happen without error when called without exception-style
+ // flavor data, or even from an exception handler when the exception
+ // behavior is EXCEPTION_DEFAULT.
+ return flavor;
+ }
+
+ default: {
+ NOTREACHED();
+ return THREAD_STATE_NONE;
+ }
+ }
+}
+
+} // namespace
+
+namespace internal {
+
+void InitializeCPUContextX86(CPUContextX86* context,
+ thread_state_flavor_t flavor,
+ ConstThreadState state,
+ mach_msg_type_number_t state_count,
+ const x86_thread_state32_t* x86_thread_state32,
+ const x86_float_state32_t* x86_float_state32,
+ const x86_debug_state32_t* x86_debug_state32) {
+ thread_state_flavor_t set_flavor = THREAD_STATE_NONE;
+ if (flavor != THREAD_STATE_NONE) {
+ set_flavor =
+ InitializeCPUContextX86Flavor(context, flavor, state, state_count);
+ }
+
+ if (set_flavor != x86_THREAD_STATE32) {
+ InitializeCPUContextX86Thread(context, x86_thread_state32);
+ }
+ if (set_flavor != x86_FLOAT_STATE32) {
+ InitializeCPUContextX86Float(context, x86_float_state32);
+ }
+ if (set_flavor != x86_DEBUG_STATE32) {
+ InitializeCPUContextX86Debug(context, x86_debug_state32);
+ }
+}
+
+void InitializeCPUContextX86_64(CPUContextX86_64* context,
+ thread_state_flavor_t flavor,
+ ConstThreadState state,
+ mach_msg_type_number_t state_count,
+ const x86_thread_state64_t* x86_thread_state64,
+ const x86_float_state64_t* x86_float_state64,
+ const x86_debug_state64_t* x86_debug_state64) {
+ thread_state_flavor_t set_flavor = THREAD_STATE_NONE;
+ if (flavor != THREAD_STATE_NONE) {
+ set_flavor =
+ InitializeCPUContextX86_64Flavor(context, flavor, state, state_count);
+ }
+
+ if (set_flavor != x86_THREAD_STATE64) {
+ InitializeCPUContextX86_64Thread(context, x86_thread_state64);
+ }
+ if (set_flavor != x86_FLOAT_STATE64) {
+ InitializeCPUContextX86_64Float(context, x86_float_state64);
+ }
+ if (set_flavor != x86_DEBUG_STATE64) {
+ InitializeCPUContextX86_64Debug(context, x86_debug_state64);
+ }
+}
+
+} // namespace internal
+
+#endif
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac.h b/chromium/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac.h
new file mode 100644
index 00000000000..1d016cf5f7d
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac.h
@@ -0,0 +1,116 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MAC_SNAPSHOT_CPU_CONTEXT_MAC_H_
+#define CRASHPAD_SNAPSHOT_MAC_SNAPSHOT_CPU_CONTEXT_MAC_H_
+
+#include <mach/mach.h>
+
+#include "build/build_config.h"
+#include "snapshot/cpu_context.h"
+#include "util/mach/mach_extensions.h"
+
+namespace crashpad {
+namespace internal {
+
+#if defined(ARCH_CPU_X86_FAMILY) || DOXYGEN
+
+//! \brief Initializes a CPUContextX86 structure from native context structures
+//! on Mac OS X.
+//!
+//! \a flavor, \a state, and \a state_count may be supplied by exception
+//! handlers in order for the \a context parameter to be initialized by the
+//! thread state received by the exception handler to the extent possible. In
+//! that case, whatever thread state specified by these three parameters will
+//! supersede \a x86_thread_state32, \a x86_float_state32, or \a
+//! x86_debug_state32. If thread state in this format is not available, \a
+//! flavor may be set to `THREAD_STATE_NONE`, and all of \a x86_thread_state32,
+//! \a x86_float_state32, and \a x86_debug_state32 will be honored.
+//!
+//! If \a flavor, \a state, and \a state_count are provided but do not contain
+//! valid values, a message will be logged and their values will be ignored as
+//! though \a flavor were specified as `THREAD_STATE_NONE`.
+//!
+//! \param[out] context The CPUContextX86 structure to initialize.
+//! \param[in] flavor The native thread state flavor of \a state. This may be
+//! `x86_THREAD_STATE32`, `x86_FLOAT_STATE32`, `x86_DEBUG_STATE32`,
+//! `x86_THREAD_STATE`, `x86_FLOAT_STATE`, or `x86_DEBUG_STATE`. It may also
+//! be `THREAD_STATE_NONE` if \a state is not supplied (and is `nullptr`).
+//! \param[in] state The native thread state, which may be a casted pointer to
+//! `x86_thread_state32_t`, `x86_float_state32_t`, `x86_debug_state32_t`,
+//! `x86_thread_state`, `x86_float_state`, or `x86_debug_state`. This
+//! parameter may be `nullptr` to not supply this data, in which case \a
+//! flavor must be `THREAD_STATE_NONE`. If a “universal” structure is used,
+//! it must carry 32-bit state data of the correct type.
+//! \param[in] state_count The number of `natural_t`-sized (`int`-sized) units
+//! in \a state. This may be 0 if \a state is `nullptr`.
+//! \param[in] x86_thread_state32 The state of the thread’s integer registers.
+//! \param[in] x86_float_state32 The state of the thread’s floating-point
+//! registers.
+//! \param[in] x86_debug_state32 The state of the thread’s debug registers.
+void InitializeCPUContextX86(CPUContextX86* context,
+ thread_state_flavor_t flavor,
+ ConstThreadState state,
+ mach_msg_type_number_t state_count,
+ const x86_thread_state32_t* x86_thread_state32,
+ const x86_float_state32_t* x86_float_state32,
+ const x86_debug_state32_t* x86_debug_state32);
+
+//! \brief Initializes a CPUContextX86_64 structure from native context
+//! structures on Mac OS X.
+//!
+//! \a flavor, \a state, and \a state_count may be supplied by exception
+//! handlers in order for the \a context parameter to be initialized by the
+//! thread state received by the exception handler to the extent possible. In
+//! that case, whatever thread state specified by these three parameters will
+//! supersede \a x86_thread_state64, \a x86_float_state64, or \a
+//! x86_debug_state64. If thread state in this format is not available, \a
+//! flavor may be set to `THREAD_STATE_NONE`, and all of \a x86_thread_state64,
+//! \a x86_float_state64, and \a x86_debug_state64 will be honored.
+//!
+//! If \a flavor, \a state, and \a state_count are provided but do not contain
+//! valid values, a message will be logged and their values will be ignored as
+//! though \a flavor were specified as `THREAD_STATE_NONE`.
+//!
+//! \param[out] context The CPUContextX86_64 structure to initialize.
+//! \param[in] flavor The native thread state flavor of \a state. This may be
+//! `x86_THREAD_STATE64`, `x86_FLOAT_STATE64`, `x86_DEBUG_STATE64`,
+//! `x86_THREAD_STATE`, `x86_FLOAT_STATE`, or `x86_DEBUG_STATE`. It may also
+//! be `THREAD_STATE_NONE` if \a state is not supplied (and is `nullptr`).
+//! \param[in] state The native thread state, which may be a casted pointer to
+//! `x86_thread_state64_t`, `x86_float_state64_t`, `x86_debug_state64_t`,
+//! `x86_thread_state`, `x86_float_state`, or `x86_debug_state`. This
+//! parameter may be `nullptr` to not supply this data, in which case \a
+//! flavor must be `THREAD_STATE_NONE`. If a “universal” structure is used,
+//! it must carry 64-bit state data of the correct type.
+//! \param[in] state_count The number of `int`-sized units in \a state. This may
+//! be 0 if \a state is `nullptr`.
+//! \param[in] x86_thread_state64 The state of the thread’s integer registers.
+//! \param[in] x86_float_state64 The state of the thread’s floating-point
+//! registers.
+//! \param[in] x86_debug_state64 The state of the thread’s debug registers.
+void InitializeCPUContextX86_64(CPUContextX86_64* context,
+ thread_state_flavor_t flavor,
+ ConstThreadState state,
+ mach_msg_type_number_t state_count,
+ const x86_thread_state64_t* x86_thread_state64,
+ const x86_float_state64_t* x86_float_state64,
+ const x86_debug_state64_t* x86_debug_state64);
+
+#endif
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MAC_SNAPSHOT_CPU_CONTEXT_MAC_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac_test.cc
new file mode 100644
index 00000000000..844c9f6e9a1
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/cpu_context_mac_test.cc
@@ -0,0 +1,421 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/mac/cpu_context_mac.h"
+
+#include <mach/mach.h>
+
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+#if defined(ARCH_CPU_X86_FAMILY)
+
+TEST(CPUContextMac, InitializeContextX86) {
+ x86_thread_state32_t x86_thread_state32 = {};
+ x86_float_state32_t x86_float_state32 = {};
+ x86_debug_state32_t x86_debug_state32 = {};
+ x86_thread_state32.__eax = 1;
+ x86_float_state32.__fpu_ftw = 2;
+ x86_debug_state32.__dr0 = 3;
+
+ // Test the simple case, where everything in the CPUContextX86 argument is set
+ // directly from the supplied thread, float, and debug state parameters.
+ {
+ CPUContextX86 cpu_context_x86 = {};
+ internal::InitializeCPUContextX86(&cpu_context_x86,
+ THREAD_STATE_NONE,
+ nullptr,
+ 0,
+ &x86_thread_state32,
+ &x86_float_state32,
+ &x86_debug_state32);
+ EXPECT_EQ(1u, cpu_context_x86.eax);
+ EXPECT_EQ(2u, cpu_context_x86.fxsave.ftw);
+ EXPECT_EQ(3u, cpu_context_x86.dr0);
+ }
+
+ // Supply context in a CPU-specific “flavor” parameter expected to be used
+ // instead of the supplied thread, float, or debug state parameters. Do this
+ // once for each of the three valid flavors. This simulates how
+ // InitializeCPUContextX86() might be used to initialize the context in an
+ // exception handler, where the exception handler may have received the
+ // “flavor” parameter and this context should be used to initialize the
+ // CPUContextX86.
+
+ {
+ x86_thread_state32_t alt_x86_thread_state32 = {};
+ alt_x86_thread_state32.__eax = 4;
+
+ CPUContextX86 cpu_context_x86 = {};
+ internal::InitializeCPUContextX86(
+ &cpu_context_x86,
+ x86_THREAD_STATE32,
+ reinterpret_cast<natural_t*>(&alt_x86_thread_state32),
+ x86_THREAD_STATE32_COUNT,
+ &x86_thread_state32,
+ &x86_float_state32,
+ &x86_debug_state32);
+ EXPECT_EQ(4u, cpu_context_x86.eax);
+ EXPECT_EQ(2u, cpu_context_x86.fxsave.ftw);
+ EXPECT_EQ(3u, cpu_context_x86.dr0);
+ }
+
+ {
+ x86_float_state32_t alt_x86_float_state32 = {};
+ alt_x86_float_state32.__fpu_ftw = 5;
+
+ CPUContextX86 cpu_context_x86 = {};
+ internal::InitializeCPUContextX86(
+ &cpu_context_x86,
+ x86_FLOAT_STATE32,
+ reinterpret_cast<natural_t*>(&alt_x86_float_state32),
+ x86_FLOAT_STATE32_COUNT,
+ &x86_thread_state32,
+ &x86_float_state32,
+ &x86_debug_state32);
+ EXPECT_EQ(1u, cpu_context_x86.eax);
+ EXPECT_EQ(5u, cpu_context_x86.fxsave.ftw);
+ EXPECT_EQ(3u, cpu_context_x86.dr0);
+ }
+
+ {
+ x86_debug_state32_t alt_x86_debug_state32 = {};
+ alt_x86_debug_state32.__dr0 = 6;
+
+ CPUContextX86 cpu_context_x86 = {};
+ internal::InitializeCPUContextX86(
+ &cpu_context_x86,
+ x86_DEBUG_STATE32,
+ reinterpret_cast<natural_t*>(&alt_x86_debug_state32),
+ x86_DEBUG_STATE32_COUNT,
+ &x86_thread_state32,
+ &x86_float_state32,
+ &x86_debug_state32);
+ EXPECT_EQ(1u, cpu_context_x86.eax);
+ EXPECT_EQ(2u, cpu_context_x86.fxsave.ftw);
+ EXPECT_EQ(6u, cpu_context_x86.dr0);
+ }
+
+ // Supply context in a universal “flavor” parameter expected to be used
+ // instead of the supplied thread, float, or debug state parameters. The
+ // universal format allows an exception handler to be registered to receive
+ // thread, float, or debug state without having to know in advance whether it
+ // will be receiving the state from a 32-bit or 64-bit process. For
+ // CPUContextX86, only the 32-bit form is supported.
+
+ {
+ x86_thread_state x86_thread_state_3264 = {};
+ x86_thread_state_3264.tsh.flavor = x86_THREAD_STATE32;
+ x86_thread_state_3264.tsh.count = x86_THREAD_STATE32_COUNT;
+ x86_thread_state_3264.uts.ts32.__eax = 7;
+
+ CPUContextX86 cpu_context_x86 = {};
+ internal::InitializeCPUContextX86(
+ &cpu_context_x86,
+ x86_THREAD_STATE,
+ reinterpret_cast<natural_t*>(&x86_thread_state_3264),
+ x86_THREAD_STATE_COUNT,
+ &x86_thread_state32,
+ &x86_float_state32,
+ &x86_debug_state32);
+ EXPECT_EQ(7u, cpu_context_x86.eax);
+ EXPECT_EQ(2u, cpu_context_x86.fxsave.ftw);
+ EXPECT_EQ(3u, cpu_context_x86.dr0);
+ }
+
+ {
+ x86_float_state x86_float_state_3264 = {};
+ x86_float_state_3264.fsh.flavor = x86_FLOAT_STATE32;
+ x86_float_state_3264.fsh.count = x86_FLOAT_STATE32_COUNT;
+ x86_float_state_3264.ufs.fs32.__fpu_ftw = 8;
+
+ CPUContextX86 cpu_context_x86 = {};
+ internal::InitializeCPUContextX86(
+ &cpu_context_x86,
+ x86_FLOAT_STATE,
+ reinterpret_cast<natural_t*>(&x86_float_state_3264),
+ x86_FLOAT_STATE_COUNT,
+ &x86_thread_state32,
+ &x86_float_state32,
+ &x86_debug_state32);
+ EXPECT_EQ(1u, cpu_context_x86.eax);
+ EXPECT_EQ(8u, cpu_context_x86.fxsave.ftw);
+ EXPECT_EQ(3u, cpu_context_x86.dr0);
+ }
+
+ {
+ x86_debug_state x86_debug_state_3264 = {};
+ x86_debug_state_3264.dsh.flavor = x86_DEBUG_STATE32;
+ x86_debug_state_3264.dsh.count = x86_DEBUG_STATE32_COUNT;
+ x86_debug_state_3264.uds.ds32.__dr0 = 9;
+
+ CPUContextX86 cpu_context_x86 = {};
+ internal::InitializeCPUContextX86(
+ &cpu_context_x86,
+ x86_DEBUG_STATE,
+ reinterpret_cast<natural_t*>(&x86_debug_state_3264),
+ x86_DEBUG_STATE_COUNT,
+ &x86_thread_state32,
+ &x86_float_state32,
+ &x86_debug_state32);
+ EXPECT_EQ(1u, cpu_context_x86.eax);
+ EXPECT_EQ(2u, cpu_context_x86.fxsave.ftw);
+ EXPECT_EQ(9u, cpu_context_x86.dr0);
+ }
+
+ // Supply inappropriate “flavor” contexts to test that
+ // InitializeCPUContextX86() detects the problem and refuses to use the
+ // supplied “flavor” context, falling back to the thread, float, and debug
+ // states.
+
+ {
+ x86_thread_state64_t x86_thread_state64 = {};
+
+ CPUContextX86 cpu_context_x86 = {};
+ internal::InitializeCPUContextX86(
+ &cpu_context_x86,
+ x86_THREAD_STATE64,
+ reinterpret_cast<natural_t*>(&x86_thread_state64),
+ x86_THREAD_STATE64_COUNT,
+ &x86_thread_state32,
+ &x86_float_state32,
+ &x86_debug_state32);
+ EXPECT_EQ(1u, cpu_context_x86.eax);
+ EXPECT_EQ(2u, cpu_context_x86.fxsave.ftw);
+ EXPECT_EQ(3u, cpu_context_x86.dr0);
+ }
+
+ {
+ x86_thread_state x86_thread_state_3264 = {};
+ x86_thread_state_3264.tsh.flavor = x86_THREAD_STATE64;
+ x86_thread_state_3264.tsh.count = x86_THREAD_STATE64_COUNT;
+
+ CPUContextX86 cpu_context_x86 = {};
+ internal::InitializeCPUContextX86(
+ &cpu_context_x86,
+ x86_THREAD_STATE,
+ reinterpret_cast<natural_t*>(&x86_thread_state_3264),
+ x86_THREAD_STATE_COUNT,
+ &x86_thread_state32,
+ &x86_float_state32,
+ &x86_debug_state32);
+ EXPECT_EQ(1u, cpu_context_x86.eax);
+ EXPECT_EQ(2u, cpu_context_x86.fxsave.ftw);
+ EXPECT_EQ(3u, cpu_context_x86.dr0);
+ }
+}
+
+TEST(CPUContextMac, InitializeContextX86_64) {
+ x86_thread_state64_t x86_thread_state64 = {};
+ x86_float_state64_t x86_float_state64 = {};
+ x86_debug_state64_t x86_debug_state64 = {};
+ x86_thread_state64.__rax = 10;
+ x86_float_state64.__fpu_ftw = 11;
+ x86_debug_state64.__dr0 = 12;
+
+ // Test the simple case, where everything in the CPUContextX86_64 argument is
+ // set directly from the supplied thread, float, and debug state parameters.
+ {
+ CPUContextX86_64 cpu_context_x86_64 = {};
+ internal::InitializeCPUContextX86_64(&cpu_context_x86_64,
+ THREAD_STATE_NONE,
+ nullptr,
+ 0,
+ &x86_thread_state64,
+ &x86_float_state64,
+ &x86_debug_state64);
+ EXPECT_EQ(10u, cpu_context_x86_64.rax);
+ EXPECT_EQ(11u, cpu_context_x86_64.fxsave.ftw);
+ EXPECT_EQ(12u, cpu_context_x86_64.dr0);
+ }
+
+ // Supply context in a CPU-specific “flavor” parameter expected to be used
+ // instead of the supplied thread, float, or debug state parameters. Do this
+ // once for each of the three valid flavors. This simulates how
+ // InitializeCPUContextX86_64() might be used to initialize the context in an
+ // exception handler, where the exception handler may have received the
+ // “flavor” parameter and this context should be used to initialize the
+ // CPUContextX86_64.
+
+ {
+ x86_thread_state64_t alt_x86_thread_state64 = {};
+ alt_x86_thread_state64.__rax = 13;
+
+ CPUContextX86_64 cpu_context_x86_64 = {};
+ internal::InitializeCPUContextX86_64(
+ &cpu_context_x86_64,
+ x86_THREAD_STATE64,
+ reinterpret_cast<natural_t*>(&alt_x86_thread_state64),
+ x86_THREAD_STATE64_COUNT,
+ &x86_thread_state64,
+ &x86_float_state64,
+ &x86_debug_state64);
+ EXPECT_EQ(13u, cpu_context_x86_64.rax);
+ EXPECT_EQ(11u, cpu_context_x86_64.fxsave.ftw);
+ EXPECT_EQ(12u, cpu_context_x86_64.dr0);
+ }
+
+ {
+ x86_float_state64_t alt_x86_float_state64 = {};
+ alt_x86_float_state64.__fpu_ftw = 14;
+
+ CPUContextX86_64 cpu_context_x86_64 = {};
+ internal::InitializeCPUContextX86_64(
+ &cpu_context_x86_64,
+ x86_FLOAT_STATE64,
+ reinterpret_cast<natural_t*>(&alt_x86_float_state64),
+ x86_FLOAT_STATE64_COUNT,
+ &x86_thread_state64,
+ &x86_float_state64,
+ &x86_debug_state64);
+ EXPECT_EQ(10u, cpu_context_x86_64.rax);
+ EXPECT_EQ(14u, cpu_context_x86_64.fxsave.ftw);
+ EXPECT_EQ(12u, cpu_context_x86_64.dr0);
+ }
+
+ {
+ x86_debug_state64_t alt_x86_debug_state64 = {};
+ alt_x86_debug_state64.__dr0 = 15;
+
+ CPUContextX86_64 cpu_context_x86_64 = {};
+ internal::InitializeCPUContextX86_64(
+ &cpu_context_x86_64,
+ x86_DEBUG_STATE64,
+ reinterpret_cast<natural_t*>(&alt_x86_debug_state64),
+ x86_DEBUG_STATE64_COUNT,
+ &x86_thread_state64,
+ &x86_float_state64,
+ &x86_debug_state64);
+ EXPECT_EQ(10u, cpu_context_x86_64.rax);
+ EXPECT_EQ(11u, cpu_context_x86_64.fxsave.ftw);
+ EXPECT_EQ(15u, cpu_context_x86_64.dr0);
+ }
+
+ // Supply context in a universal “flavor” parameter expected to be used
+ // instead of the supplied thread, float, or debug state parameters. The
+ // universal format allows an exception handler to be registered to receive
+ // thread, float, or debug state without having to know in advance whether it
+ // will be receiving the state from a 32-bit or 64-bit process. For
+ // CPUContextX86_64, only the 64-bit form is supported.
+
+ {
+ x86_thread_state x86_thread_state_3264 = {};
+ x86_thread_state_3264.tsh.flavor = x86_THREAD_STATE64;
+ x86_thread_state_3264.tsh.count = x86_THREAD_STATE64_COUNT;
+ x86_thread_state_3264.uts.ts64.__rax = 16;
+
+ CPUContextX86_64 cpu_context_x86_64 = {};
+ internal::InitializeCPUContextX86_64(
+ &cpu_context_x86_64,
+ x86_THREAD_STATE,
+ reinterpret_cast<natural_t*>(&x86_thread_state_3264),
+ x86_THREAD_STATE_COUNT,
+ &x86_thread_state64,
+ &x86_float_state64,
+ &x86_debug_state64);
+ EXPECT_EQ(16u, cpu_context_x86_64.rax);
+ EXPECT_EQ(11u, cpu_context_x86_64.fxsave.ftw);
+ EXPECT_EQ(12u, cpu_context_x86_64.dr0);
+ }
+
+ {
+ x86_float_state x86_float_state_3264 = {};
+ x86_float_state_3264.fsh.flavor = x86_FLOAT_STATE64;
+ x86_float_state_3264.fsh.count = x86_FLOAT_STATE64_COUNT;
+ x86_float_state_3264.ufs.fs64.__fpu_ftw = 17;
+
+ CPUContextX86_64 cpu_context_x86_64 = {};
+ internal::InitializeCPUContextX86_64(
+ &cpu_context_x86_64,
+ x86_FLOAT_STATE,
+ reinterpret_cast<natural_t*>(&x86_float_state_3264),
+ x86_FLOAT_STATE_COUNT,
+ &x86_thread_state64,
+ &x86_float_state64,
+ &x86_debug_state64);
+ EXPECT_EQ(10u, cpu_context_x86_64.rax);
+ EXPECT_EQ(17u, cpu_context_x86_64.fxsave.ftw);
+ EXPECT_EQ(12u, cpu_context_x86_64.dr0);
+ }
+
+ {
+ x86_debug_state x86_debug_state_3264 = {};
+ x86_debug_state_3264.dsh.flavor = x86_DEBUG_STATE64;
+ x86_debug_state_3264.dsh.count = x86_DEBUG_STATE64_COUNT;
+ x86_debug_state_3264.uds.ds64.__dr0 = 18;
+
+ CPUContextX86_64 cpu_context_x86_64 = {};
+ internal::InitializeCPUContextX86_64(
+ &cpu_context_x86_64,
+ x86_DEBUG_STATE,
+ reinterpret_cast<natural_t*>(&x86_debug_state_3264),
+ x86_DEBUG_STATE_COUNT,
+ &x86_thread_state64,
+ &x86_float_state64,
+ &x86_debug_state64);
+ EXPECT_EQ(10u, cpu_context_x86_64.rax);
+ EXPECT_EQ(11u, cpu_context_x86_64.fxsave.ftw);
+ EXPECT_EQ(18u, cpu_context_x86_64.dr0);
+ }
+
+ // Supply inappropriate “flavor” contexts to test that
+ // InitializeCPUContextX86() detects the problem and refuses to use the
+ // supplied “flavor” context, falling back to the thread, float, and debug
+ // states.
+
+ {
+ x86_thread_state32_t x86_thread_state32 = {};
+
+ CPUContextX86_64 cpu_context_x86_64 = {};
+ internal::InitializeCPUContextX86_64(
+ &cpu_context_x86_64,
+ x86_THREAD_STATE32,
+ reinterpret_cast<natural_t*>(&x86_thread_state32),
+ x86_THREAD_STATE32_COUNT,
+ &x86_thread_state64,
+ &x86_float_state64,
+ &x86_debug_state64);
+ EXPECT_EQ(10u, cpu_context_x86_64.rax);
+ EXPECT_EQ(11u, cpu_context_x86_64.fxsave.ftw);
+ EXPECT_EQ(12u, cpu_context_x86_64.dr0);
+ }
+
+ {
+ x86_thread_state x86_thread_state_3264 = {};
+ x86_thread_state_3264.tsh.flavor = x86_THREAD_STATE32;
+ x86_thread_state_3264.tsh.count = x86_THREAD_STATE32_COUNT;
+
+ CPUContextX86_64 cpu_context_x86_64 = {};
+ internal::InitializeCPUContextX86_64(
+ &cpu_context_x86_64,
+ x86_THREAD_STATE,
+ reinterpret_cast<natural_t*>(&x86_thread_state_3264),
+ x86_THREAD_STATE_COUNT,
+ &x86_thread_state64,
+ &x86_float_state64,
+ &x86_debug_state64);
+ EXPECT_EQ(10u, cpu_context_x86_64.rax);
+ EXPECT_EQ(11u, cpu_context_x86_64.fxsave.ftw);
+ EXPECT_EQ(12u, cpu_context_x86_64.dr0);
+ }
+}
+
+#endif
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/exception_snapshot_mac.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/exception_snapshot_mac.cc
new file mode 100644
index 00000000000..b5725d38e93
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/exception_snapshot_mac.cc
@@ -0,0 +1,253 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/mac/exception_snapshot_mac.h"
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "snapshot/mac/cpu_context_mac.h"
+#include "snapshot/mac/process_reader.h"
+#include "util/mach/exception_behaviors.h"
+#include "util/mach/exception_types.h"
+#include "util/mach/symbolic_constants_mach.h"
+#include "util/numeric/safe_assignment.h"
+
+namespace crashpad {
+namespace internal {
+
+ExceptionSnapshotMac::ExceptionSnapshotMac()
+ : ExceptionSnapshot(),
+ context_union_(),
+ context_(),
+ codes_(),
+ thread_id_(0),
+ exception_address_(0),
+ exception_(0),
+ exception_code_0_(0),
+ initialized_() {
+}
+
+ExceptionSnapshotMac::~ExceptionSnapshotMac() {
+}
+
+bool ExceptionSnapshotMac::Initialize(ProcessReader* process_reader,
+ exception_behavior_t behavior,
+ thread_t exception_thread,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t flavor,
+ ConstThreadState state,
+ mach_msg_type_number_t state_count) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ codes_.push_back(exception);
+ for (mach_msg_type_number_t code_index = 0;
+ code_index < code_count;
+ ++code_index) {
+ codes_.push_back(code[code_index]);
+ }
+
+ exception_ = exception;
+ mach_exception_code_t exception_code_0 = code[0];
+
+ if (exception_ == EXC_CRASH) {
+ exception_ = ExcCrashRecoverOriginalException(
+ exception_code_0, &exception_code_0, nullptr);
+
+ if (exception_ == EXC_CRASH ||
+ exception_ == EXC_RESOURCE ||
+ exception_ == EXC_GUARD) {
+ // EXC_CRASH should never be wrapped in another EXC_CRASH.
+ //
+ // EXC_RESOURCE and EXC_GUARD are software exceptions that are never
+ // wrapped in EXC_CRASH. The only time EXC_CRASH is generated is for
+ // processes exiting due to an unhandled core-generating signal or being
+ // killed by SIGKILL for code-signing reasons. Neither of these applies to
+ // EXC_RESOURCE or EXC_GUARD. See 10.10 xnu-2782.1.97/bsd/kern/kern_exit.c
+ // proc_prepareexit(). Receiving these exception types wrapped in
+ // EXC_CRASH would lose information because their code[0] uses all 64 bits
+ // (see below) and the code[0] recovered from EXC_CRASH only contains 20
+ // significant bits.
+ LOG(WARNING) << base::StringPrintf(
+ "exception %s invalid in EXC_CRASH",
+ ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric)
+ .c_str());
+ }
+ }
+
+ // The operations that follow put exception_code_0 (a mach_exception_code_t,
+ // a typedef for int64_t) into exception_code_0_ (a uint32_t). The range
+ // checks and bit shifts involved need the same signedness on both sides to
+ // work properly.
+ const uint64_t unsigned_exception_code_0 = exception_code_0;
+
+ // ExceptionInfo() returns code[0] as a 32-bit value, but exception_code_0 is
+ // a 64-bit value. The best treatment for this inconsistency depends on the
+ // exception type.
+ if (exception_ == EXC_RESOURCE || exception_ == EXC_GUARD) {
+ // All 64 bits of code[0] are significant for these exceptions. See
+ // <mach/exc_resource.h> for EXC_RESOURCE and 10.10
+ // xnu-2782.1.97/bsd/kern/kern_guarded.c fd_guard_ast() for EXC_GUARD.
+ // code[0] is structured similarly for these two exceptions.
+ //
+ // EXC_RESOURCE: see <kern/exc_resource.h>. The resource type and “flavor”
+ // together define the resource and are in the highest bits. The resource
+ // limit is in the lowest bits.
+ //
+ // EXC_GUARD: see 10.10 xnu-2782.1.97/osfmk/ipc/mach_port.c
+ // mach_port_guard_exception() and xnu-2782.1.97/bsd/kern/kern_guarded.c
+ // fd_guard_ast(). The guard type (GUARD_TYPE_MACH_PORT or GUARD_TYPE_FD)
+ // and “flavor” (from the mach_port_guard_exception_codes or
+ // guard_exception_codes enums) are in the highest bits. The violating Mach
+ // port name or file descriptor number is in the lowest bits.
+
+ // If MACH_EXCEPTION_CODES is not set in |behavior|, code[0] will only carry
+ // 32 significant bits, and the interesting high bits will have been
+ // truncated.
+ if (!ExceptionBehaviorHasMachExceptionCodes(behavior)) {
+ LOG(WARNING) << base::StringPrintf(
+ "behavior %s invalid for exception %s",
+ ExceptionBehaviorToString(
+ behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str(),
+ ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric)
+ .c_str());
+ }
+
+ // Include the more-significant information from the high bits of code[0] in
+ // the value to be returned by ExceptionInfo(). The full value of codes[0]
+ // including the less-significant lower bits is still available via Codes().
+ exception_code_0_ = unsigned_exception_code_0 >> 32;
+ } else {
+ // For other exceptions, code[0]’s values never exceed 32 bits.
+ if (!base::IsValueInRangeForNumericType<decltype(exception_code_0_)>(
+ unsigned_exception_code_0)) {
+ LOG(WARNING) << base::StringPrintf("exception_code_0 0x%llx out of range",
+ unsigned_exception_code_0);
+ }
+ exception_code_0_ = unsigned_exception_code_0;
+ }
+
+ const ProcessReader::Thread* thread = nullptr;
+ for (const ProcessReader::Thread& loop_thread : process_reader->Threads()) {
+ if (exception_thread == loop_thread.port) {
+ thread = &loop_thread;
+ break;
+ }
+ }
+
+ if (!thread) {
+ LOG(ERROR) << "exception_thread not found in task";
+ return false;
+ }
+
+ thread_id_ = thread->id;
+
+ // Normally, the exception address is present in code[1] for EXC_BAD_ACCESS
+ // exceptions, but not for other types of exceptions.
+ bool code_1_is_exception_address = exception_ == EXC_BAD_ACCESS;
+
+#if defined(ARCH_CPU_X86_FAMILY)
+ if (process_reader->Is64Bit()) {
+ context_.architecture = kCPUArchitectureX86_64;
+ context_.x86_64 = &context_union_.x86_64;
+ InitializeCPUContextX86_64(context_.x86_64,
+ flavor,
+ state,
+ state_count,
+ &thread->thread_context.t64,
+ &thread->float_context.f64,
+ &thread->debug_context.d64);
+ } else {
+ context_.architecture = kCPUArchitectureX86;
+ context_.x86 = &context_union_.x86;
+ InitializeCPUContextX86(context_.x86,
+ flavor,
+ state,
+ state_count,
+ &thread->thread_context.t32,
+ &thread->float_context.f32,
+ &thread->debug_context.d32);
+ }
+
+ // For x86 and x86_64 EXC_BAD_ACCESS exceptions, some code[0] values indicate
+ // that code[1] does not (or may not) carry the exception address:
+ // EXC_I386_GPFLT (10.9.5 xnu-2422.115.4/osfmk/i386/trap.c user_trap() for
+ // T_GENERAL_PROTECTION) and the oddball (VM_PROT_READ | VM_PROT_EXECUTE)
+ // which collides with EXC_I386_BOUNDFLT (10.9.5
+ // xnu-2422.115.4/osfmk/i386/fpu.c fpextovrflt()). Other EXC_BAD_ACCESS
+ // exceptions come through 10.9.5 xnu-2422.115.4/osfmk/i386/trap.c
+ // user_page_fault_continue() and do contain the exception address in code[1].
+ if (exception_ == EXC_BAD_ACCESS &&
+ (exception_code_0_ == EXC_I386_GPFLT ||
+ exception_code_0_ == (VM_PROT_READ | VM_PROT_EXECUTE))) {
+ code_1_is_exception_address = false;
+ }
+#endif
+
+ if (code_1_is_exception_address) {
+ if (process_reader->Is64Bit() &&
+ !ExceptionBehaviorHasMachExceptionCodes(behavior)) {
+ // If code[1] is an address from a 64-bit process, the exception must have
+ // been received with MACH_EXCEPTION_CODES or the address will have been
+ // truncated.
+ LOG(WARNING) << base::StringPrintf(
+ "behavior %s invalid for exception %s code %d in 64-bit process",
+ ExceptionBehaviorToString(
+ behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str(),
+ ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric)
+ .c_str(),
+ exception_code_0_);
+ }
+ exception_address_ = code[1];
+ } else {
+ exception_address_ = context_.InstructionPointer();
+ }
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+const CPUContext* ExceptionSnapshotMac::Context() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return &context_;
+}
+
+uint64_t ExceptionSnapshotMac::ThreadID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return thread_id_;
+}
+
+uint32_t ExceptionSnapshotMac::Exception() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return exception_;
+}
+
+uint32_t ExceptionSnapshotMac::ExceptionInfo() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return exception_code_0_;
+}
+
+uint64_t ExceptionSnapshotMac::ExceptionAddress() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return exception_address_;
+}
+
+const std::vector<uint64_t>& ExceptionSnapshotMac::Codes() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return codes_;
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/exception_snapshot_mac.h b/chromium/third_party/crashpad/crashpad/snapshot/mac/exception_snapshot_mac.h
new file mode 100644
index 00000000000..ea1161c3de5
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/exception_snapshot_mac.h
@@ -0,0 +1,93 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MAC_EXCEPTION_SNAPSHOT_MAC_H_
+#define CRASHPAD_SNAPSHOT_MAC_EXCEPTION_SNAPSHOT_MAC_H_
+
+#include <mach/mach.h>
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "build/build_config.h"
+#include "snapshot/cpu_context.h"
+#include "snapshot/exception_snapshot.h"
+#include "util/mach/mach_extensions.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+
+class ProcessReader;
+
+namespace internal {
+
+//! \brief An ExceptionSnapshot of an exception sustained by a running (or
+//! crashed) process on a Mac OS X system.
+class ExceptionSnapshotMac final : public ExceptionSnapshot {
+ public:
+ ExceptionSnapshotMac();
+ ~ExceptionSnapshotMac() override;
+
+ //! \brief Initializes the object.
+ //!
+ //! Other than \a process_reader, the parameters may be passed directly
+ //! through from a Mach exception handler.
+ //!
+ //! \param[in] process_reader A ProcessReader for the task that sustained the
+ //! exception.
+ //!
+ //! \return `true` if the snapshot could be created, `false` otherwise with
+ //! an appropriate message logged.
+ bool Initialize(ProcessReader* process_reader,
+ exception_behavior_t behavior,
+ thread_t exception_thread,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t flavor,
+ ConstThreadState state,
+ mach_msg_type_number_t state_count);
+
+ // ExceptionSnapshot:
+
+ const CPUContext* Context() const override;
+ uint64_t ThreadID() const override;
+ uint32_t Exception() const override;
+ uint32_t ExceptionInfo() const override;
+ uint64_t ExceptionAddress() const override;
+ const std::vector<uint64_t>& Codes() const override;
+
+ private:
+#if defined(ARCH_CPU_X86_FAMILY)
+ union {
+ CPUContextX86 x86;
+ CPUContextX86_64 x86_64;
+ } context_union_;
+#endif
+ CPUContext context_;
+ std::vector<uint64_t> codes_;
+ uint64_t thread_id_;
+ uint64_t exception_address_;
+ exception_type_t exception_;
+ uint32_t exception_code_0_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExceptionSnapshotMac);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MAC_EXCEPTION_SNAPSHOT_MAC_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader.cc
new file mode 100644
index 00000000000..6c6831bdaea
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader.cc
@@ -0,0 +1,173 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/mac/mach_o_image_annotations_reader.h"
+
+#include <mach-o/loader.h>
+#include <mach/mach.h>
+
+#include <utility>
+
+#include "base/logging.h"
+#include "client/crashpad_info.h"
+#include "client/simple_string_dictionary.h"
+#include "snapshot/mac/mach_o_image_reader.h"
+#include "snapshot/mac/process_reader.h"
+#include "util/mach/task_memory.h"
+#include "util/stdlib/strnlen.h"
+
+namespace crashpad {
+
+MachOImageAnnotationsReader::MachOImageAnnotationsReader(
+ ProcessReader* process_reader,
+ const MachOImageReader* image_reader,
+ const std::string& name)
+ : name_(name),
+ process_reader_(process_reader),
+ image_reader_(image_reader) {
+}
+
+std::vector<std::string> MachOImageAnnotationsReader::Vector() const {
+ std::vector<std::string> vector_annotations;
+
+ ReadCrashReporterClientAnnotations(&vector_annotations);
+ ReadDyldErrorStringAnnotation(&vector_annotations);
+
+ return vector_annotations;
+}
+
+std::map<std::string, std::string> MachOImageAnnotationsReader::SimpleMap()
+ const {
+ std::map<std::string, std::string> simple_map_annotations;
+
+ ReadCrashpadSimpleAnnotations(&simple_map_annotations);
+
+ return simple_map_annotations;
+}
+
+void MachOImageAnnotationsReader::ReadCrashReporterClientAnnotations(
+ std::vector<std::string>* vector_annotations) const {
+ mach_vm_address_t crash_info_address;
+ const process_types::section* crash_info_section =
+ image_reader_->GetSectionByName(
+ SEG_DATA, "__crash_info", &crash_info_address);
+ if (!crash_info_section) {
+ return;
+ }
+
+ process_types::crashreporter_annotations_t crash_info;
+ if (crash_info_section->size < crash_info.ExpectedSize(process_reader_)) {
+ LOG(WARNING) << "small crash info section size " << crash_info_section->size
+ << " in " << name_;
+ return;
+ }
+
+ if (!crash_info.Read(process_reader_, crash_info_address)) {
+ LOG(WARNING) << "could not read crash info from " << name_;
+ return;
+ }
+
+ if (crash_info.version != 4) {
+ LOG(WARNING) << "unexpected crash info version " << crash_info.version
+ << " in " << name_;
+ return;
+ }
+
+ // This number was totally made up out of nowhere, but it seems prudent to
+ // enforce some limit.
+ const size_t kMaxMessageSize = 1024;
+ if (crash_info.message) {
+ std::string message;
+ if (process_reader_->Memory()->
+ ReadCStringSizeLimited(
+ crash_info.message, kMaxMessageSize, &message)) {
+ vector_annotations->push_back(message);
+ } else {
+ LOG(WARNING) << "could not read crash message in " << name_;
+ }
+ }
+
+ if (crash_info.message2) {
+ std::string message;
+ if (process_reader_->Memory()->
+ ReadCStringSizeLimited(
+ crash_info.message2, kMaxMessageSize, &message)) {
+ vector_annotations->push_back(message);
+ } else {
+ LOG(WARNING) << "could not read crash message 2 in " << name_;
+ }
+ }
+}
+
+void MachOImageAnnotationsReader::ReadDyldErrorStringAnnotation(
+ std::vector<std::string>* vector_annotations) const {
+ // dyld stores its error string at the external symbol for |const char
+ // error_string[1024]|. See 10.9.5 dyld-239.4/src/dyld.cpp error_string.
+ if (image_reader_->FileType() != MH_DYLINKER) {
+ return;
+ }
+
+ mach_vm_address_t error_string_address;
+ if (!image_reader_->LookUpExternalDefinedSymbol("_error_string",
+ &error_string_address)) {
+ return;
+ }
+
+ std::string message;
+ // 1024 here is distinct from kMaxMessageSize above, because it refers to a
+ // precisely-sized buffer inside dyld.
+ if (process_reader_->Memory()->
+ ReadCStringSizeLimited(error_string_address, 1024, &message)) {
+ if (!message.empty()) {
+ vector_annotations->push_back(message);
+ }
+ } else {
+ LOG(WARNING) << "could not read dylinker error string from " << name_;
+ }
+}
+
+void MachOImageAnnotationsReader::ReadCrashpadSimpleAnnotations(
+ std::map<std::string, std::string>* simple_map_annotations) const {
+ process_types::CrashpadInfo crashpad_info;
+ if (!image_reader_->GetCrashpadInfo(&crashpad_info)) {
+ return;
+ }
+
+ if (!crashpad_info.simple_annotations) {
+ return;
+ }
+
+ std::vector<SimpleStringDictionary::Entry>
+ simple_annotations(SimpleStringDictionary::num_entries);
+ if (!process_reader_->Memory()
+ ->Read(crashpad_info.simple_annotations,
+ simple_annotations.size() * sizeof(simple_annotations[0]),
+ &simple_annotations[0])) {
+ LOG(WARNING) << "could not read simple annotations from " << name_;
+ return;
+ }
+
+ for (const auto& entry : simple_annotations) {
+ size_t key_length = strnlen(entry.key, sizeof(entry.key));
+ if (key_length) {
+ std::string key(entry.key, key_length);
+ std::string value(entry.value, strnlen(entry.value, sizeof(entry.value)));
+ if (!simple_map_annotations->insert(std::make_pair(key, value)).second) {
+ LOG(INFO) << "duplicate simple annotation " << key << " in " << name_;
+ }
+ }
+ }
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader.h
new file mode 100644
index 00000000000..6b66e8f96a0
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader.h
@@ -0,0 +1,93 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_ANNOTATIONS_READER_H_
+#define CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_ANNOTATIONS_READER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "snapshot/mac/process_types.h"
+
+namespace crashpad {
+
+class MachOImageReader;
+class ProcessReader;
+
+//! \brief A reader for annotations stored in a Mach-O image mapped into another
+//! process.
+//!
+//! These annotations are stored for the benefit of crash reporters, and provide
+//! information though to be potentially useful for crash analysis. This class
+//! can decode annotations stored in these formats:
+//! - CrashpadInfo. This format is used by Crashpad clients. The “simple
+//! annotations” are recovered from any module with a compatible data
+//! section, and are included in the annotations returned by SimpleMap().
+//! - `CrashReporterClient.h`’s `crashreporter_annotations_t`. This format is
+//! used by Apple code. The `message` and `message2` fields can be recovered
+//! from any module with a compatible data section, and are included in the
+//! annotations returned by Vector().
+//! - `dyld`’s `error_string`. This format is used exclusively by dyld,
+//! typically for fatal errors. This string can be recovered from any
+//! `MH_DYLINKER`-type module with this symbol, and is included in the
+//! annotations returned by Vector().
+class MachOImageAnnotationsReader {
+ public:
+ //! \brief Constructs an object.
+ //!
+ //! \param[in] process_reader The reader for the remote process.
+ //! \param[in] image_reader The MachOImageReader for the Mach-O image file
+ //! contained within the remote process.
+ //! \param[in] name The module’s name, a string to be used in logged messages.
+ //! This string is for diagnostic purposes only, and may be empty.
+ MachOImageAnnotationsReader(ProcessReader* process_reader,
+ const MachOImageReader* image_reader,
+ const std::string& name);
+
+ ~MachOImageAnnotationsReader() {}
+
+ //! \brief Returns the module’s annotations that are organized as a vector of
+ //! strings.
+ std::vector<std::string> Vector() const;
+
+ //! \brief Returns the module’s annotations that are organized as key-value
+ //! pairs, where all keys and values are strings.
+ std::map<std::string, std::string> SimpleMap() const;
+
+ private:
+ // Reades crashreporter_annotations_t::message and
+ // crashreporter_annotations_t::message2 on behalf of Vector().
+ void ReadCrashReporterClientAnnotations(
+ std::vector<std::string>* vector_annotations) const;
+
+ // Reads dyld_error_string on behalf of Vector().
+ void ReadDyldErrorStringAnnotation(
+ std::vector<std::string>* vector_annotations) const;
+
+ // Reads CrashpadInfo::simple_annotations_ on behalf of SimpleMap().
+ void ReadCrashpadSimpleAnnotations(
+ std::map<std::string, std::string>* simple_map_annotations) const;
+
+ std::string name_;
+ ProcessReader* process_reader_; // weak
+ const MachOImageReader* image_reader_; // weak
+
+ DISALLOW_COPY_AND_ASSIGN(MachOImageAnnotationsReader);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_ANNOTATIONS_READER_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader_test.cc
new file mode 100644
index 00000000000..b9a782ca8b7
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader_test.cc
@@ -0,0 +1,344 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/mac/mach_o_image_annotations_reader.h"
+
+#include <dlfcn.h>
+#include <mach/mach.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "client/crashpad_info.h"
+#include "client/simple_string_dictionary.h"
+#include "gtest/gtest.h"
+#include "snapshot/mac/process_reader.h"
+#include "test/errors.h"
+#include "test/mac/mach_errors.h"
+#include "test/mac/mach_multiprocess.h"
+#include "util/file/file_io.h"
+#include "util/mac/mac_util.h"
+#include "util/mach/exc_server_variants.h"
+#include "util/mach/exception_ports.h"
+#include "util/mach/mach_extensions.h"
+#include "util/mach/mach_message.h"
+#include "util/mach/mach_message_server.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+class TestMachOImageAnnotationsReader final
+ : public MachMultiprocess,
+ public UniversalMachExcServer::Interface {
+ public:
+ enum TestType {
+ // Don’t crash, just test the CrashpadInfo interface.
+ kDontCrash = 0,
+
+ // The child process should crash by calling abort(). The parent verifies
+ // that the system libraries set the expected annotations.
+ kCrashAbort,
+
+ // The child process should crash by setting DYLD_INSERT_LIBRARIES to
+ // contain a nonexistent library. The parent verifies that dyld sets the
+ // expected annotations.
+ kCrashDyld,
+ };
+
+ explicit TestMachOImageAnnotationsReader(TestType test_type)
+ : MachMultiprocess(),
+ UniversalMachExcServer::Interface(),
+ test_type_(test_type) {
+ }
+
+ ~TestMachOImageAnnotationsReader() {}
+
+ // UniversalMachExcServer::Interface:
+ kern_return_t CatchMachException(exception_behavior_t behavior,
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_complex_request) override {
+ *destroy_complex_request = true;
+
+ EXPECT_EQ(ChildTask(), task);
+
+ ProcessReader process_reader;
+ bool rv = process_reader.Initialize(task);
+ if (!rv) {
+ ADD_FAILURE();
+ } else {
+ const std::vector<ProcessReader::Module>& modules =
+ process_reader.Modules();
+ std::vector<std::string> all_annotations_vector;
+ for (const ProcessReader::Module& module : modules) {
+ if (module.reader) {
+ MachOImageAnnotationsReader module_annotations_reader(
+ &process_reader, module.reader, module.name);
+ std::vector<std::string> module_annotations_vector =
+ module_annotations_reader.Vector();
+ all_annotations_vector.insert(all_annotations_vector.end(),
+ module_annotations_vector.begin(),
+ module_annotations_vector.end());
+ } else {
+ EXPECT_TRUE(module.reader);
+ }
+ }
+
+ // Mac OS X 10.6 doesn’t have support for CrashReporter annotations
+ // (CrashReporterClient.h), so don’t look for any special annotations in
+ // that version.
+ int mac_os_x_minor_version = MacOSXMinorVersion();
+ if (mac_os_x_minor_version > 7) {
+ EXPECT_GE(all_annotations_vector.size(), 1u);
+
+ const char* expected_annotation = nullptr;
+ switch (test_type_) {
+ case kCrashAbort:
+ // The child process calls abort(), so the expected annotation
+ // reflects this, with a string set by 10.7.5
+ // Libc-763.13/stdlib/abort-fbsd.c abort(). This string is still
+ // present in 10.9.5 Libc-997.90.3/stdlib/FreeBSD/abort.c abort(),
+ // but because abort() tests to see if a message is already set and
+ // something else in Libc will have set a message, this string is
+ // not the expectation on 10.9 or higher. Instead, after fork(), the
+ // child process has a message indicating that a fork() without
+ // exec() occurred. See 10.9.5 Libc-997.90.3/sys/_libc_fork_child.c
+ // _libc_fork_child().
+ expected_annotation =
+ mac_os_x_minor_version <= 8
+ ? "abort() called"
+ : "crashed on child side of fork pre-exec";
+ break;
+
+ case kCrashDyld:
+ // This is independent of dyld’s error_string, which is tested
+ // below.
+ expected_annotation = "dyld: launch, loading dependent libraries";
+ break;
+
+ default:
+ ADD_FAILURE();
+ break;
+ }
+
+ size_t expected_annotation_length = strlen(expected_annotation);
+ bool found = false;
+ for (const std::string& annotation : all_annotations_vector) {
+ // Look for the expectation as a leading susbtring, because the actual
+ // string that dyld uses will have the contents of the
+ // DYLD_INSERT_LIBRARIES environment variable appended to it on Mac
+ // OS X 10.10.
+ if (annotation.substr(0, expected_annotation_length) ==
+ expected_annotation) {
+ found = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(found);
+ }
+
+ // dyld exposes its error_string at least as far back as Mac OS X 10.4.
+ if (test_type_ == kCrashDyld) {
+ const char kExpectedAnnotation[] = "could not load inserted library";
+ size_t expected_annotation_length = strlen(kExpectedAnnotation);
+ bool found = false;
+ for (const std::string& annotation : all_annotations_vector) {
+ // Look for the expectation as a leading substring, because the actual
+ // string will contain the library’s pathname and, on Mac OS X 10.9
+ // and later, a reason.
+ if (annotation.substr(0, expected_annotation_length) ==
+ kExpectedAnnotation) {
+ found = true;
+ break;
+ }
+ }
+
+ EXPECT_TRUE(found);
+ }
+ }
+
+ return ExcServerSuccessfulReturnValue(behavior, false);
+ }
+
+ private:
+ // MachMultiprocess:
+
+ void MachMultiprocessParent() override {
+ ProcessReader process_reader;
+ ASSERT_TRUE(process_reader.Initialize(ChildTask()));
+
+ // Wait for the child process to indicate that it’s done setting up its
+ // annotations via the CrashpadInfo interface.
+ char c;
+ CheckedReadFile(ReadPipeHandle(), &c, sizeof(c));
+
+ // Verify the “simple map” annotations set via the CrashpadInfo interface.
+ const std::vector<ProcessReader::Module>& modules =
+ process_reader.Modules();
+ std::map<std::string, std::string> all_annotations_simple_map;
+ for (const ProcessReader::Module& module : modules) {
+ MachOImageAnnotationsReader module_annotations_reader(
+ &process_reader, module.reader, module.name);
+ std::map<std::string, std::string> module_annotations_simple_map =
+ module_annotations_reader.SimpleMap();
+ all_annotations_simple_map.insert(module_annotations_simple_map.begin(),
+ module_annotations_simple_map.end());
+ }
+
+ EXPECT_GE(all_annotations_simple_map.size(), 5u);
+ EXPECT_EQ("crash", all_annotations_simple_map["#TEST# pad"]);
+ EXPECT_EQ("value", all_annotations_simple_map["#TEST# key"]);
+ EXPECT_EQ("y", all_annotations_simple_map["#TEST# x"]);
+ EXPECT_EQ("shorter", all_annotations_simple_map["#TEST# longer"]);
+ EXPECT_EQ("", all_annotations_simple_map["#TEST# empty_value"]);
+
+ // Tell the child process that it’s permitted to crash.
+ CheckedWriteFile(WritePipeHandle(), &c, sizeof(c));
+
+ if (test_type_ != kDontCrash) {
+ // Handle the child’s crash. Further validation will be done in
+ // CatchMachException().
+ UniversalMachExcServer universal_mach_exc_server(this);
+
+ mach_msg_return_t mr =
+ MachMessageServer::Run(&universal_mach_exc_server,
+ LocalPort(),
+ MACH_MSG_OPTION_NONE,
+ MachMessageServer::kOneShot,
+ MachMessageServer::kReceiveLargeError,
+ kMachMessageTimeoutWaitIndefinitely);
+ EXPECT_EQ(MACH_MSG_SUCCESS, mr)
+ << MachErrorMessage(mr, "MachMessageServer::Run");
+
+ switch (test_type_) {
+ case kCrashAbort:
+ SetExpectedChildTermination(kTerminationSignal, SIGABRT);
+ break;
+
+ case kCrashDyld:
+ // dyld fatal errors result in the execution of an int3 instruction on
+ // x86 and a trap instruction on ARM, both of which raise SIGTRAP.
+ // 10.9.5 dyld-239.4/src/dyldStartup.s _dyld_fatal_error.
+ SetExpectedChildTermination(kTerminationSignal, SIGTRAP);
+ break;
+
+ default:
+ FAIL();
+ break;
+ }
+ }
+ }
+
+ void MachMultiprocessChild() override {
+ CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo();
+
+ // This is “leaked” to crashpad_info.
+ SimpleStringDictionary* simple_annotations = new SimpleStringDictionary();
+ simple_annotations->SetKeyValue("#TEST# pad", "break");
+ simple_annotations->SetKeyValue("#TEST# key", "value");
+ simple_annotations->SetKeyValue("#TEST# pad", "crash");
+ simple_annotations->SetKeyValue("#TEST# x", "y");
+ simple_annotations->SetKeyValue("#TEST# longer", "shorter");
+ simple_annotations->SetKeyValue("#TEST# empty_value", "");
+
+ crashpad_info->set_simple_annotations(simple_annotations);
+
+ // Tell the parent that the environment has been set up.
+ char c = '\0';
+ CheckedWriteFile(WritePipeHandle(), &c, sizeof(c));
+
+ // Wait for the parent to indicate that it’s safe to crash.
+ CheckedReadFile(ReadPipeHandle(), &c, sizeof(c));
+
+ // Direct an exception message to the exception server running in the
+ // parent.
+ ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask,
+ mach_task_self());
+ ASSERT_TRUE(exception_ports.SetExceptionPort(
+ EXC_MASK_CRASH, RemotePort(), EXCEPTION_DEFAULT, THREAD_STATE_NONE));
+
+ switch (test_type_) {
+ case kDontCrash:
+ break;
+
+ case kCrashAbort:
+ abort();
+ break;
+
+ case kCrashDyld: {
+ // Set DYLD_INSERT_LIBRARIES to contain a library that does not exist.
+ // Unable to load it, dyld will abort with a fatal error.
+ ASSERT_EQ(
+ 0,
+ setenv(
+ "DYLD_INSERT_LIBRARIES", "/var/empty/NoDirectory/NoLibrary", 1))
+ << ErrnoMessage("setenv");
+
+ // The actual executable doesn’t matter very much, because dyld won’t
+ // ever launch it. It just needs to be an executable that uses dyld as
+ // its LC_LOAD_DYLINKER (all normal executables do). /usr/bin/true is on
+ // every system, so use it.
+ ASSERT_EQ(0, execl("/usr/bin/true", "true", nullptr))
+ << ErrnoMessage("execl");
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ TestType test_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestMachOImageAnnotationsReader);
+};
+
+TEST(MachOImageAnnotationsReader, DontCrash) {
+ TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(
+ TestMachOImageAnnotationsReader::kDontCrash);
+ test_mach_o_image_annotations_reader.Run();
+}
+
+TEST(MachOImageAnnotationsReader, CrashAbort) {
+ TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(
+ TestMachOImageAnnotationsReader::kCrashAbort);
+ test_mach_o_image_annotations_reader.Run();
+}
+
+TEST(MachOImageAnnotationsReader, CrashDyld) {
+ TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(
+ TestMachOImageAnnotationsReader::kCrashDyld);
+ test_mach_o_image_annotations_reader.Run();
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.cc
new file mode 100644
index 00000000000..cb85baabfd9
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.cc
@@ -0,0 +1,723 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/mac/mach_o_image_reader.h"
+
+#include <mach-o/loader.h>
+#include <mach-o/nlist.h>
+#include <string.h>
+
+#include <limits>
+#include <vector>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "client/crashpad_info.h"
+#include "snapshot/mac/mach_o_image_segment_reader.h"
+#include "snapshot/mac/mach_o_image_symbol_table_reader.h"
+#include "snapshot/mac/process_reader.h"
+#include "util/mac/checked_mach_address_range.h"
+
+namespace {
+
+const uint32_t kInvalidSegmentIndex = std::numeric_limits<uint32_t>::max();
+
+} // namespace
+
+namespace crashpad {
+
+MachOImageReader::MachOImageReader()
+ : segments_(),
+ segment_map_(),
+ module_name_(),
+ module_info_(),
+ dylinker_name_(),
+ uuid_(),
+ address_(0),
+ size_(0),
+ slide_(0),
+ source_version_(0),
+ symtab_command_(),
+ dysymtab_command_(),
+ symbol_table_(),
+ id_dylib_command_(),
+ process_reader_(nullptr),
+ file_type_(0),
+ initialized_(),
+ symbol_table_initialized_() {
+}
+
+MachOImageReader::~MachOImageReader() {
+}
+
+bool MachOImageReader::Initialize(ProcessReader* process_reader,
+ mach_vm_address_t address,
+ const std::string& name) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ process_reader_ = process_reader;
+ address_ = address;
+ module_name_ = name;
+
+ module_info_ =
+ base::StringPrintf(", module %s, address 0x%llx", name.c_str(), address);
+
+ process_types::mach_header mach_header;
+ if (!mach_header.Read(process_reader, address)) {
+ LOG(WARNING) << "could not read mach_header" << module_info_;
+ return false;
+ }
+
+ const bool is_64_bit = process_reader->Is64Bit();
+ const uint32_t kExpectedMagic = is_64_bit ? MH_MAGIC_64 : MH_MAGIC;
+ if (mach_header.magic != kExpectedMagic) {
+ LOG(WARNING) << base::StringPrintf("unexpected mach_header::magic 0x%08x",
+ mach_header.magic) << module_info_;
+ return false;
+ }
+
+ switch (mach_header.filetype) {
+ case MH_EXECUTE:
+ case MH_DYLIB:
+ case MH_DYLINKER:
+ case MH_BUNDLE:
+ file_type_ = mach_header.filetype;
+ break;
+ default:
+ LOG(WARNING) << base::StringPrintf(
+ "unexpected mach_header::filetype 0x%08x",
+ mach_header.filetype) << module_info_;
+ return false;
+ }
+
+ const uint32_t kExpectedSegmentCommand =
+ is_64_bit ? LC_SEGMENT_64 : LC_SEGMENT;
+ const uint32_t kUnexpectedSegmentCommand =
+ is_64_bit ? LC_SEGMENT : LC_SEGMENT_64;
+
+ const struct {
+ // Which method to call when encountering a load command matching |command|.
+ bool (MachOImageReader::*function)(mach_vm_address_t, const std::string&);
+
+ // The minimum size that may be allotted to store the load command.
+ size_t size;
+
+ // The load command to match.
+ uint32_t command;
+
+ // True if the load command must not appear more than one time.
+ bool singleton;
+ } kLoadCommandReaders[] = {
+ {
+ &MachOImageReader::ReadSegmentCommand,
+ process_types::segment_command::ExpectedSize(process_reader),
+ kExpectedSegmentCommand,
+ false,
+ },
+ {
+ &MachOImageReader::ReadSymTabCommand,
+ process_types::symtab_command::ExpectedSize(process_reader),
+ LC_SYMTAB,
+ true,
+ },
+ {
+ &MachOImageReader::ReadDySymTabCommand,
+ process_types::symtab_command::ExpectedSize(process_reader),
+ LC_DYSYMTAB,
+ true,
+ },
+ {
+ &MachOImageReader::ReadIdDylibCommand,
+ process_types::dylib_command::ExpectedSize(process_reader),
+ LC_ID_DYLIB,
+ true,
+ },
+ {
+ &MachOImageReader::ReadDylinkerCommand,
+ process_types::dylinker_command::ExpectedSize(process_reader),
+ LC_LOAD_DYLINKER,
+ true,
+ },
+ {
+ &MachOImageReader::ReadDylinkerCommand,
+ process_types::dylinker_command::ExpectedSize(process_reader),
+ LC_ID_DYLINKER,
+ true,
+ },
+ {
+ &MachOImageReader::ReadUUIDCommand,
+ process_types::uuid_command::ExpectedSize(process_reader),
+ LC_UUID,
+ true,
+ },
+ {
+ &MachOImageReader::ReadSourceVersionCommand,
+ process_types::source_version_command::ExpectedSize(process_reader),
+ LC_SOURCE_VERSION,
+ true,
+ },
+
+ // When reading a 64-bit process, no 32-bit segment commands should be
+ // present, and vice-versa.
+ {
+ &MachOImageReader::ReadUnexpectedCommand,
+ process_types::load_command::ExpectedSize(process_reader),
+ kUnexpectedSegmentCommand,
+ false,
+ },
+ };
+
+ // This vector is parallel to the kLoadCommandReaders array, and tracks
+ // whether a singleton load command matching the |command| field has been
+ // found yet.
+ std::vector<uint32_t> singleton_indices(arraysize(kLoadCommandReaders),
+ kInvalidSegmentIndex);
+
+ size_t offset = mach_header.Size();
+ const mach_vm_address_t kLoadCommandAddressLimit =
+ address + offset + mach_header.sizeofcmds;
+
+ for (uint32_t load_command_index = 0;
+ load_command_index < mach_header.ncmds;
+ ++load_command_index) {
+ mach_vm_address_t load_command_address = address + offset;
+ std::string load_command_info = base::StringPrintf(", load command %u/%u%s",
+ load_command_index,
+ mach_header.ncmds,
+ module_info_.c_str());
+
+ process_types::load_command load_command;
+
+ // Make sure that the basic load command structure doesn’t overflow the
+ // space allotted for load commands.
+ if (load_command_address + load_command.ExpectedSize(process_reader) >
+ kLoadCommandAddressLimit) {
+ LOG(WARNING) << base::StringPrintf(
+ "load_command at 0x%llx exceeds sizeofcmds 0x%x",
+ load_command_address,
+ mach_header.sizeofcmds) << load_command_info;
+ return false;
+ }
+
+ if (!load_command.Read(process_reader, load_command_address)) {
+ LOG(WARNING) << "could not read load_command" << load_command_info;
+ return false;
+ }
+
+ load_command_info = base::StringPrintf(", load command 0x%x %u/%u%s",
+ load_command.cmd,
+ load_command_index,
+ mach_header.ncmds,
+ module_info_.c_str());
+
+ // Now that the load command’s stated size is known, make sure that it
+ // doesn’t overflow the space allotted for load commands.
+ if (load_command_address + load_command.cmdsize >
+ kLoadCommandAddressLimit) {
+ LOG(WARNING)
+ << base::StringPrintf(
+ "load_command at 0x%llx cmdsize 0x%x exceeds sizeofcmds 0x%x",
+ load_command_address,
+ load_command.cmdsize,
+ mach_header.sizeofcmds) << load_command_info;
+ return false;
+ }
+
+ for (size_t reader_index = 0;
+ reader_index < arraysize(kLoadCommandReaders);
+ ++reader_index) {
+ if (load_command.cmd != kLoadCommandReaders[reader_index].command) {
+ continue;
+ }
+
+ if (load_command.cmdsize < kLoadCommandReaders[reader_index].size) {
+ LOG(WARNING) << base::StringPrintf(
+ "load command cmdsize 0x%x insufficient for 0x%zx",
+ load_command.cmdsize,
+ kLoadCommandReaders[reader_index].size)
+ << load_command_info;
+ return false;
+ }
+
+ if (kLoadCommandReaders[reader_index].singleton) {
+ if (singleton_indices[reader_index] != kInvalidSegmentIndex) {
+ LOG(WARNING) << "duplicate load command at "
+ << singleton_indices[reader_index] << load_command_info;
+ return false;
+ }
+
+ singleton_indices[reader_index] = load_command_index;
+ }
+
+ if (!((this)->*(kLoadCommandReaders[reader_index].function))(
+ load_command_address, load_command_info)) {
+ return false;
+ }
+
+ break;
+ }
+
+ offset += load_command.cmdsize;
+ }
+
+ // Now that the slide is known, push it into the segments.
+ for (MachOImageSegmentReader* segment : segments_) {
+ segment->SetSlide(slide_);
+
+ // This was already checked for the unslid values while the segments were
+ // read, but now it’s possible to check the slid values too. The individual
+ // sections don’t need to be checked because they were verified to be
+ // contained within their respective segments when the segments were read.
+ mach_vm_address_t slid_segment_address = segment->Address();
+ mach_vm_size_t slid_segment_size = segment->Size();
+ CheckedMachAddressRange slid_segment_range(
+ process_reader_->Is64Bit(), slid_segment_address, slid_segment_size);
+ if (!slid_segment_range.IsValid()) {
+ LOG(WARNING) << base::StringPrintf(
+ "invalid slid segment range 0x%llx + 0x%llx, "
+ "segment ",
+ slid_segment_address,
+ slid_segment_size) << segment->Name() << module_info_;
+ return false;
+ }
+ }
+
+ if (!segment_map_.count(SEG_TEXT)) {
+ // The __TEXT segment is required. Even a module with no executable code
+ // will have a __TEXT segment encompassing the Mach-O header and load
+ // commands. Without a __TEXT segment, |size_| will not have been computed.
+ LOG(WARNING) << "no " SEG_TEXT " segment" << module_info_;
+ return false;
+ }
+
+ if (mach_header.filetype == MH_DYLIB && !id_dylib_command_) {
+ // This doesn’t render a module unusable, it’s just weird and worth noting.
+ LOG(INFO) << "no LC_ID_DYLIB" << module_info_;
+ }
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+const MachOImageSegmentReader* MachOImageReader::GetSegmentByName(
+ const std::string& segment_name) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ const auto& iterator = segment_map_.find(segment_name);
+ if (iterator == segment_map_.end()) {
+ return nullptr;
+ }
+
+ const MachOImageSegmentReader* segment = segments_[iterator->second];
+ return segment;
+}
+
+const process_types::section* MachOImageReader::GetSectionByName(
+ const std::string& segment_name,
+ const std::string& section_name,
+ mach_vm_address_t* address) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ const MachOImageSegmentReader* segment = GetSegmentByName(segment_name);
+ if (!segment) {
+ return nullptr;
+ }
+
+ return segment->GetSectionByName(section_name, address);
+}
+
+const process_types::section* MachOImageReader::GetSectionAtIndex(
+ size_t index,
+ const MachOImageSegmentReader** containing_segment,
+ mach_vm_address_t* address) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ static_assert(NO_SECT == 0, "NO_SECT must be zero");
+ if (index == NO_SECT) {
+ LOG(WARNING) << "section index " << index << " out of range";
+ return nullptr;
+ }
+
+ // Switch to a more comfortable 0-based index.
+ size_t local_index = index - 1;
+
+ for (const MachOImageSegmentReader* segment : segments_) {
+ size_t nsects = segment->nsects();
+ if (local_index < nsects) {
+ const process_types::section* section =
+ segment->GetSectionAtIndex(local_index, address);
+
+ if (containing_segment) {
+ *containing_segment = segment;
+ }
+
+ return section;
+ }
+
+ local_index -= nsects;
+ }
+
+ LOG(WARNING) << "section index " << index << " out of range";
+ return nullptr;
+}
+
+bool MachOImageReader::LookUpExternalDefinedSymbol(
+ const std::string& name,
+ mach_vm_address_t* value) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ if (symbol_table_initialized_.is_uninitialized()) {
+ InitializeSymbolTable();
+ }
+
+ if (!symbol_table_initialized_.is_valid() || !symbol_table_) {
+ return false;
+ }
+
+ const MachOImageSymbolTableReader::SymbolInformation* symbol_info =
+ symbol_table_->LookUpExternalDefinedSymbol(name);
+ if (!symbol_info) {
+ return false;
+ }
+
+ if (symbol_info->section == NO_SECT) {
+ // This is an absolute (N_ABS) symbol, which requires no further validation
+ // or processing.
+ *value = symbol_info->value;
+ return true;
+ }
+
+ // This is a symbol defined in a particular section, so make sure that it’s
+ // valid for that section and fix it up for any “slide” as needed.
+
+ mach_vm_address_t section_address;
+ const MachOImageSegmentReader* segment;
+ const process_types::section* section =
+ GetSectionAtIndex(symbol_info->section, &segment, &section_address);
+ if (!section) {
+ return false;
+ }
+
+ mach_vm_address_t slid_value =
+ symbol_info->value + (segment->SegmentSlides() ? slide_ : 0);
+
+ // The __mh_execute_header (_MH_EXECUTE_SYM) symbol is weird. In
+ // position-independent executables, it shows up in the symbol table as a
+ // symbol in section 1, although it’s not really in that section. It points to
+ // the mach_header[_64], which is the beginning of the __TEXT segment, and the
+ // __text section normally begins after the load commands in the __TEXT
+ // segment. The range check below will fail for this symbol, because it’s not
+ // really in the section it claims to be in. See Xcode 5.1
+ // ld64-236.3/src/ld/OutputFile.cpp ld::tool::OutputFile::buildSymbolTable().
+ // There, ld takes symbols that refer to anything in the mach_header[_64] and
+ // marks them as being in section 1. Here, section 1 is treated in this same
+ // special way as long as it’s in the __TEXT segment that begins at the start
+ // of the image, which is normally the case, and as long as the symbol’s value
+ // is the base of the image.
+ //
+ // This only happens for PIE executables, because __mh_execute_header needs
+ // to slide. In non-PIE executables, __mh_execute_header is an absolute
+ // symbol.
+ CheckedMachAddressRange section_range(
+ process_reader_->Is64Bit(), section_address, section->size);
+ if (!section_range.ContainsValue(slid_value) &&
+ !(symbol_info->section == 1 && segment->Name() == SEG_TEXT &&
+ slid_value == Address())) {
+ std::string section_name_full =
+ MachOImageSegmentReader::SegmentAndSectionNameString(section->segname,
+ section->sectname);
+ LOG(WARNING) << base::StringPrintf(
+ "symbol %s (0x%llx) outside of section %s (0x%llx + "
+ "0x%llx)",
+ name.c_str(),
+ slid_value,
+ section_name_full.c_str(),
+ section_address,
+ section->size) << module_info_;
+ return false;
+ }
+
+ *value = slid_value;
+ return true;
+}
+
+uint32_t MachOImageReader::DylibVersion() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ DCHECK_EQ(FileType(), implicit_cast<uint32_t>(MH_DYLIB));
+
+ if (id_dylib_command_) {
+ return id_dylib_command_->dylib_current_version;
+ }
+
+ // In case this was a weird dylib without an LC_ID_DYLIB command.
+ return 0;
+}
+
+void MachOImageReader::UUID(crashpad::UUID* uuid) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ memcpy(uuid, &uuid_, sizeof(uuid_));
+}
+
+bool MachOImageReader::GetCrashpadInfo(
+ process_types::CrashpadInfo* crashpad_info) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ mach_vm_address_t crashpad_info_address;
+ const process_types::section* crashpad_info_section =
+ GetSectionByName(SEG_DATA, "__crashpad_info", &crashpad_info_address);
+ if (!crashpad_info_section) {
+ return false;
+ }
+
+ if (crashpad_info_section->size <
+ crashpad_info->ExpectedSize(process_reader_)) {
+ LOG(WARNING) << "small crashpad info section size "
+ << crashpad_info_section->size << module_info_;
+ return false;
+ }
+
+ if (!crashpad_info->Read(process_reader_, crashpad_info_address)) {
+ LOG(WARNING) << "could not read crashpad info" << module_info_;
+ return false;
+ }
+
+ if (crashpad_info->signature != CrashpadInfo::kSignature ||
+ crashpad_info->size != crashpad_info_section->size ||
+ crashpad_info->version < 1) {
+ LOG(WARNING) << "unexpected crashpad info data" << module_info_;
+ return false;
+ }
+
+ return true;
+}
+
+template <typename T>
+bool MachOImageReader::ReadLoadCommand(mach_vm_address_t load_command_address,
+ const std::string& load_command_info,
+ uint32_t expected_load_command_id,
+ T* load_command) {
+ if (!load_command->Read(process_reader_, load_command_address)) {
+ LOG(WARNING) << "could not read load command" << load_command_info;
+ return false;
+ }
+
+ DCHECK_GE(load_command->cmdsize, load_command->Size());
+ DCHECK_EQ(load_command->cmd, expected_load_command_id);
+ return true;
+}
+
+bool MachOImageReader::ReadSegmentCommand(
+ mach_vm_address_t load_command_address,
+ const std::string& load_command_info) {
+ MachOImageSegmentReader* segment = new MachOImageSegmentReader();
+ size_t segment_index = segments_.size();
+ segments_.push_back(segment); // Takes ownership.
+
+ if (!segment->Initialize(process_reader_,
+ load_command_address,
+ load_command_info,
+ module_name_,
+ file_type_)) {
+ segments_.pop_back();
+ return false;
+ }
+
+ // At this point, the segment itself is considered valid, but if one of the
+ // next checks fails, it will render the module invalid. If any of the next
+ // checks fail, this method should return false, but it doesn’t need to bother
+ // removing the segment from segments_. The segment will be properly released
+ // when the image is destroyed, and the image won’t be usable because
+ // initialization won’t have completed. Most importantly, leaving the segment
+ // in segments_ means that no other structures (such as perhaps segment_map_)
+ // become inconsistent or require cleanup.
+
+ const std::string segment_name = segment->Name();
+ const auto insert_result =
+ segment_map_.insert(std::make_pair(segment_name, segment_index));
+ if (!insert_result.second) {
+ LOG(WARNING) << base::StringPrintf("duplicate %s segment at %zu and %zu",
+ segment_name.c_str(),
+ insert_result.first->second,
+ segment_index) << load_command_info;
+ return false;
+ }
+
+ mach_vm_size_t vmsize = segment->vmsize();
+
+ if (segment_name == SEG_TEXT) {
+ if (vmsize == 0) {
+ LOG(WARNING) << "zero-sized " SEG_TEXT " segment" << load_command_info;
+ return false;
+ }
+
+ mach_vm_size_t fileoff = segment->fileoff();
+ if (fileoff != 0) {
+ LOG(WARNING) << base::StringPrintf(
+ SEG_TEXT " segment has unexpected fileoff 0x%llx",
+ fileoff) << load_command_info;
+ return false;
+ }
+
+ size_ = vmsize;
+
+ // The slide is computed as the difference between the __TEXT segment’s
+ // preferred and actual load addresses. This is the same way that dyld
+ // computes slide. See 10.9.2 dyld-239.4/src/dyldInitialization.cpp
+ // slideOfMainExecutable().
+ slide_ = address_ - segment->vmaddr();
+ }
+
+ return true;
+}
+
+bool MachOImageReader::ReadSymTabCommand(mach_vm_address_t load_command_address,
+ const std::string& load_command_info) {
+ symtab_command_.reset(new process_types::symtab_command());
+ return ReadLoadCommand(load_command_address,
+ load_command_info,
+ LC_SYMTAB,
+ symtab_command_.get());
+}
+
+bool MachOImageReader::ReadDySymTabCommand(
+ mach_vm_address_t load_command_address,
+ const std::string& load_command_info) {
+ dysymtab_command_.reset(new process_types::dysymtab_command());
+ return ReadLoadCommand(load_command_address,
+ load_command_info,
+ LC_DYSYMTAB,
+ dysymtab_command_.get());
+}
+
+bool MachOImageReader::ReadIdDylibCommand(
+ mach_vm_address_t load_command_address,
+ const std::string& load_command_info) {
+ if (file_type_ != MH_DYLIB) {
+ LOG(WARNING) << base::StringPrintf(
+ "LC_ID_DYLIB inappropriate in non-dylib file type 0x%x",
+ file_type_) << load_command_info;
+ return false;
+ }
+
+ DCHECK(!id_dylib_command_);
+ id_dylib_command_.reset(new process_types::dylib_command());
+ return ReadLoadCommand(load_command_address,
+ load_command_info,
+ LC_ID_DYLIB,
+ id_dylib_command_.get());
+}
+
+bool MachOImageReader::ReadDylinkerCommand(
+ mach_vm_address_t load_command_address,
+ const std::string& load_command_info) {
+ if (file_type_ != MH_EXECUTE && file_type_ != MH_DYLINKER) {
+ LOG(WARNING) << base::StringPrintf(
+ "LC_LOAD_DYLINKER/LC_ID_DYLINKER inappropriate in file "
+ "type 0x%x",
+ file_type_) << load_command_info;
+ return false;
+ }
+
+ const uint32_t kExpectedCommand =
+ file_type_ == MH_DYLINKER ? LC_ID_DYLINKER : LC_LOAD_DYLINKER;
+ process_types::dylinker_command dylinker_command;
+ if (!ReadLoadCommand(load_command_address,
+ load_command_info,
+ kExpectedCommand,
+ &dylinker_command)) {
+ return false;
+ }
+
+ if (!process_reader_->Memory()->ReadCStringSizeLimited(
+ load_command_address + dylinker_command.name,
+ dylinker_command.cmdsize - dylinker_command.name,
+ &dylinker_name_)) {
+ LOG(WARNING) << "could not read dylinker_command name" << load_command_info;
+ return false;
+ }
+
+ return true;
+}
+
+bool MachOImageReader::ReadUUIDCommand(mach_vm_address_t load_command_address,
+ const std::string& load_command_info) {
+ process_types::uuid_command uuid_command;
+ if (!ReadLoadCommand(
+ load_command_address, load_command_info, LC_UUID, &uuid_command)) {
+ return false;
+ }
+
+ uuid_.InitializeFromBytes(uuid_command.uuid);
+ return true;
+}
+
+bool MachOImageReader::ReadSourceVersionCommand(
+ mach_vm_address_t load_command_address,
+ const std::string& load_command_info) {
+ process_types::source_version_command source_version_command;
+ if (!ReadLoadCommand(load_command_address,
+ load_command_info,
+ LC_SOURCE_VERSION,
+ &source_version_command)) {
+ return false;
+ }
+
+ source_version_ = source_version_command.version;
+ return true;
+}
+
+bool MachOImageReader::ReadUnexpectedCommand(
+ mach_vm_address_t load_command_address,
+ const std::string& load_command_info) {
+ LOG(WARNING) << "unexpected load command" << load_command_info;
+ return false;
+}
+
+void MachOImageReader::InitializeSymbolTable() const {
+ DCHECK(symbol_table_initialized_.is_uninitialized());
+ symbol_table_initialized_.set_invalid();
+
+ if (!symtab_command_) {
+ // It’s technically valid for there to be no LC_SYMTAB, and in that case,
+ // any symbol lookups should fail. Mark the symbol table as valid, and
+ // LookUpExternalDefinedSymbol() will understand what it means when this is
+ // valid but symbol_table_ is not present.
+ symbol_table_initialized_.set_valid();
+ return;
+ }
+
+ // Find the __LINKEDIT segment. Technically, the symbol table can be in any
+ // mapped segment, but by convention, it’s in the one named __LINKEDIT.
+ const MachOImageSegmentReader* linkedit_segment =
+ GetSegmentByName(SEG_LINKEDIT);
+ if (!linkedit_segment) {
+ LOG(WARNING) << "no " SEG_LINKEDIT " segment";
+ return;
+ }
+
+ symbol_table_.reset(new MachOImageSymbolTableReader());
+ if (!symbol_table_->Initialize(process_reader_,
+ symtab_command_.get(),
+ dysymtab_command_.get(),
+ linkedit_segment,
+ module_info_)) {
+ symbol_table_.reset();
+ return;
+ }
+
+ symbol_table_initialized_.set_valid();
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.h
new file mode 100644
index 00000000000..7047bc6a6fa
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.h
@@ -0,0 +1,354 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_READER_H_
+#define CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_READER_H_
+
+#include <mach/mach.h>
+#include <stdint.h>
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "snapshot/mac/process_types.h"
+#include "util/misc/initialization_state_dcheck.h"
+#include "util/misc/uuid.h"
+#include "util/stdlib/pointer_container.h"
+
+namespace crashpad {
+
+class MachOImageSegmentReader;
+class MachOImageSymbolTableReader;
+class ProcessReader;
+
+//! \brief A reader for Mach-O images mapped into another process.
+//!
+//! This class is capable of reading both 32-bit (`mach_header`/`MH_MAGIC`) and
+//! 64-bit (`mach_header_64`/`MH_MAGIC_64`) images based on the bitness of the
+//! remote process.
+//!
+//! \sa MachOImageAnnotationsReader
+class MachOImageReader {
+ public:
+ MachOImageReader();
+ ~MachOImageReader();
+
+ //! \brief Reads the Mach-O image file’s load commands from another process.
+ //!
+ //! This method must only be called once on an object. This method must be
+ //! called successfully before any other method in this class may be called.
+ //!
+ //! \param[in] process_reader The reader for the remote process.
+ //! \param[in] address The address, in the remote process’ address space,
+ //! where the `mach_header` or `mach_header_64` at the beginning of the
+ //! image to be read is located. This address can be determined by reading
+ //! the remote process’ dyld information (see
+ //! snapshot/mac/process_types/dyld_images.proctype).
+ //! \param[in] name The module’s name, a string to be used in logged messages.
+ //! This string is for diagnostic purposes and to relax otherwise strict
+ //! parsing rules for common modules with known defects.
+ //!
+ //! \return `true` if the image was read successfully, including all load
+ //! commands. `false` otherwise, with an appropriate message logged.
+ bool Initialize(ProcessReader* process_reader,
+ mach_vm_address_t address,
+ const std::string& name);
+
+ //! \brief Returns the Mach-O file type.
+ //!
+ //! This value comes from the `filetype` field of the `mach_header` or
+ //! `mach_header_64`. Common values include `MH_EXECUTE`, `MH_DYLIB`,
+ //! `MH_DYLINKER`, and `MH_BUNDLE`.
+ uint32_t FileType() const { return file_type_; }
+
+ //! \brief Returns the Mach-O image’s load address.
+ //!
+ //! This is the value passed as \a address to Initialize().
+ mach_vm_address_t Address() const { return address_; }
+
+ //! \brief Returns the mapped size of the Mach-O image’s `__TEXT` segment.
+ //!
+ //! Note that this is returns only the size of the `__TEXT` segment, not of
+ //! any other segment. This is because the interface only allows one load
+ //! address and size to be reported, but Mach-O image files may consist of
+ //! multiple discontiguous segments. By convention, the `__TEXT` segment is
+ //! always mapped at the beginning of a Mach-O image file, and it is the most
+ //! useful for the expected intended purpose of collecting data to obtain
+ //! stack backtraces. The implementation insists during initialization that
+ //! the `__TEXT` segment be mapped at the beginning of the file.
+ //!
+ //! In practice, discontiguous segments are only found for images that have
+ //! loaded out of the dyld shared cache, but the `__TEXT` segment’s size is
+ //! returned for modules that loaded with contiguous segments as well for
+ //! consistency.
+ mach_vm_size_t Size() const { return size_; }
+
+ //! \brief Returns the Mach-O image’s “slide,” the difference between its
+ //! actual load address and its preferred load address.
+ //!
+ //! “Slide” is computed by subtracting the `__TEXT` segment’s preferred load
+ //! address from its actual load address. It will be reported as a positive
+ //! offset when the actual load address is greater than the preferred load
+ //! address. The preferred load address is taken to be the segment’s reported
+ //! `vmaddr` value.
+ mach_vm_size_t Slide() const { return slide_; }
+
+ //! \brief Obtain segment information by segment name.
+ //!
+ //! \param[in] segment_name The name of the segment to search for, for
+ //! example, `"__TEXT"`.
+ //!
+ //! \return A pointer to the segment information if it was found, or `nullptr`
+ //! if it was not found. The caller does not take ownership; the lifetime
+ //! of the returned object is scoped to the lifetime of this
+ //! MachOImageReader object.
+ const MachOImageSegmentReader* GetSegmentByName(
+ const std::string& segment_name) const;
+
+ //! \brief Obtain section information by segment and section name.
+ //!
+ //! \param[in] segment_name The name of the segment to search for, for
+ //! example, `"__TEXT"`.
+ //! \param[in] section_name The name of the section within the segment to
+ //! search for, for example, `"__text"`.
+ //! \param[out] address The actual address that the section was loaded at in
+ //! memory, taking any “slide” into account if the section did not load at
+ //! its preferred address as stored in the Mach-O image file. This
+ //! parameter can be `nullptr`.
+ //!
+ //! \return A pointer to the section information if it was found, or `nullptr`
+ //! if it was not found. The caller does not take ownership; the lifetime
+ //! of the returned object is scoped to the lifetime of this
+ //! MachOImageReader object.
+ //!
+ //! No parameter is provided for the section’s size, because it can be
+ //! obtained from the returned process_types::section::size field.
+ //!
+ //! \note The process_types::section::addr field gives the section’s preferred
+ //! load address as stored in the Mach-O image file, and is not adjusted
+ //! for any “slide” that may have occurred when the image was loaded. Use
+ //! \a address to obtain the section’s actual load address.
+ const process_types::section* GetSectionByName(
+ const std::string& segment_name,
+ const std::string& section_name,
+ mach_vm_address_t* address) const;
+
+ //! \brief Obtain section information by section index.
+ //!
+ //! \param[in] index The index of the section to return, in the order that it
+ //! appears in the segment load commands. This is a 1-based index,
+ //! matching the section number values used for `nlist::n_sect`.
+ //! \param[out] containing_segment The segment that contains the section.
+ //! This parameter can be `nullptr`. The caller does not take ownership;
+ //! the lifetime of the returned object is scoped to the lifetime of this
+ //! MachOImageReader object.
+ //! \param[out] address The actual address that the section was loaded at in
+ //! memory, taking any “slide” into account if the section did not load at
+ //! its preferred address as stored in the Mach-O image file. This
+ //! parameter can be `nullptr`.
+ //!
+ //! \return A pointer to the section information. If \a index is out of range,
+ //! logs a warning and returns `nullptr`. The caller does not take
+ //! ownership; the lifetime of the returned object is scoped to the
+ //! lifetime of this MachOImageReader object.
+ //!
+ //! No parameter is provided for the section’s size, because it can be
+ //! obtained from the returned process_types::section::size field.
+ //!
+ //! \note The process_types::section::addr field gives the section’s preferred
+ //! load address as stored in the Mach-O image file, and is not adjusted
+ //! for any “slide” that may have occurred when the image was loaded. Use
+ //! \a address to obtain the section’s actual load address.
+ //! \note Unlike MachOImageSegmentReader::GetSectionAtIndex(), this method
+ //! accepts out-of-range values for \a index, and returns `nullptr`
+ //! instead of aborting execution upon encountering an out-of-range value.
+ //! This is because a Mach-O image file’s symbol table refers to this
+ //! per-module section index, and an out-of-range index in that case
+ //! should be treated as a data error (where the data is beyond this
+ //! code’s control) and handled non-fatally by reporting the error to the
+ //! caller.
+ const process_types::section* GetSectionAtIndex(
+ size_t index,
+ const MachOImageSegmentReader** containing_segment,
+ mach_vm_address_t* address) const;
+
+ //! \brief Looks up a symbol in the image’s symbol table.
+ //!
+ //! This method is capable of locating external defined symbols. Specifically,
+ //! this method can look up symbols that have these charcteristics:
+ //! - `N_STAB` (debugging) and `N_PEXT` (private external) must not be set.
+ //! - `N_EXT` (external) must be set.
+ //! - The type must be `N_ABS` (absolute) or `N_SECT` (defined in section).
+ //!
+ //! `N_INDR` (indirect), `N_UNDF` (undefined), and `N_PBUD` (prebound
+ //! undefined) symbols cannot be located through this mechanism.
+ //!
+ //! \param[in] name The name of the symbol to look up, “mangled” or
+ //! “decorated” appropriately. For example, use `"_main"` to look up the
+ //! symbol for the C `main()` function, and use `"__Z4Funcv"` to look up
+ //! the symbol for the C++ `Func()` function. Contrary to `dlsym()`, the
+ //! leading underscore must not be stripped when using this interface.
+ //! \param[out] value If the lookup was successful, this will be set to the
+ //! value of the symbol, adjusted for any “slide” as needed. The value can
+ //! be used as an address in the remote process’ address space where the
+ //! pointee of the symbol exists in memory.
+ //!
+ //! \return `true` if the symbol lookup was successful and the symbol was
+ //! found. `false` otherwise, including error conditions (for which a
+ //! warning message will be logged), modules without symbol tables, and
+ //! symbol names not found in the symbol table.
+ //!
+ //! \note Symbol values returned via this interface are adjusted for “slide”
+ //! as appropriate, in contrast to the underlying implementation,
+ //! MachOImageSymbolTableReader::LookUpExternalDefinedSymbol().
+ //!
+ //! \warning Symbols that are resolved by running symbol resolvers
+ //! (`.symbol_resolver`) are not properly handled by this interface. The
+ //! address of the symbol resolver is returned because that’s what shows
+ //! up in the symbol table, rather than the effective address of the
+ //! resolved symbol as used by dyld after running the resolver. The only
+ //! way to detect this situation would be to read the `LC_DYLD_INFO` or
+ //! `LC_DYLD_INFO_ONLY` load command if present and looking for the
+ //! `EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER` flag, but that would just be
+ //! able to detect symbols with a resolver, it would not be able to
+ //! resolve them from out-of-process, so it’s not currently done.
+ bool LookUpExternalDefinedSymbol(const std::string& name,
+ mach_vm_address_t* value) const;
+
+ //! \brief Returns a Mach-O dylib image’s current version.
+ //!
+ //! This information comes from the `dylib_current_version` field of a dylib’s
+ //! `LC_ID_DYLIB` load command. For dylibs without this load command, `0` will
+ //! be returned.
+ //!
+ //! This method may only be called on Mach-O images for which FileType()
+ //! returns `MH_DYLIB`.
+ uint32_t DylibVersion() const;
+
+ //! \brief Returns a Mach-O image’s source version.
+ //!
+ //! This information comes from a Mach-O image’s `LC_SOURCE_VERSION` load
+ //! command. For Mach-O images without this load command, `0` will be
+ //! returned.
+ uint64_t SourceVersion() const { return source_version_; }
+
+ //! \brief Returns a Mach-O image’s UUID.
+ //!
+ //! This information comes from a Mach-O image’s `LC_UUID` load command. For
+ //! Mach-O images without this load command, a zeroed-out UUID value will be
+ //! returned.
+ //
+ // UUID is a name in this scope (referring to this method), so the parameter’s
+ // type needs to be qualified with |crashpad::|.
+ void UUID(crashpad::UUID* uuid) const;
+
+ //! \brief Returns the dynamic linker’s pathname.
+ //!
+ //! The dynamic linker is normally /usr/lib/dyld.
+ //!
+ //! For executable images (those with file type `MH_EXECUTE`), this is the
+ //! name provided in the `LC_LOAD_DYLINKER` load command, if any. For dynamic
+ //! linker images (those with file type `MH_DYLINKER`), this is the name
+ //! provided in the `LC_ID_DYLINKER` load command. In other cases, this will
+ //! be empty.
+ std::string DylinkerName() const { return dylinker_name_; }
+
+ //! \brief Obtains the module’s CrashpadInfo structure.
+ //!
+ //! \return `true` on success, `false` on failure. If the module does not have
+ //! a `__crashpad_info` section, this will return `false` without logging
+ //! any messages. Other failures will result in messages being logged.
+ bool GetCrashpadInfo(process_types::CrashpadInfo* crashpad_info) const;
+
+ private:
+ // A generic helper routine for the other Read*Command() methods.
+ template <typename T>
+ bool ReadLoadCommand(mach_vm_address_t load_command_address,
+ const std::string& load_command_info,
+ uint32_t expected_load_command_id,
+ T* load_command);
+
+ // The Read*Command() methods are subroutines called by Initialize(). They are
+ // responsible for reading a single load command. They may update the member
+ // fields of their MachOImageReader object. If they can’t make sense of a load
+ // command, they return false.
+ bool ReadSegmentCommand(mach_vm_address_t load_command_address,
+ const std::string& load_command_info);
+ bool ReadSymTabCommand(mach_vm_address_t load_command_address,
+ const std::string& load_command_info);
+ bool ReadDySymTabCommand(mach_vm_address_t load_command_address,
+ const std::string& load_command_info);
+ bool ReadIdDylibCommand(mach_vm_address_t load_command_address,
+ const std::string& load_command_info);
+ bool ReadDylinkerCommand(mach_vm_address_t load_command_address,
+ const std::string& load_command_info);
+ bool ReadUUIDCommand(mach_vm_address_t load_command_address,
+ const std::string& load_command_info);
+ bool ReadSourceVersionCommand(mach_vm_address_t load_command_address,
+ const std::string& load_command_info);
+ bool ReadUnexpectedCommand(mach_vm_address_t load_command_address,
+ const std::string& load_command_info);
+
+ // Performs deferred initialization of the symbol table. Because a module’s
+ // symbol table is often not needed, this is not handled in Initialize(), but
+ // is done lazily, on-demand as needed.
+ //
+ // symbol_table_initialized_ will be transitioned to the appropriate state. If
+ // initialization completes successfully, this will be the valid state.
+ // Otherwise, it will be left in the invalid state and a warning message will
+ // be logged.
+ //
+ // Note that if the object contains no symbol table, symbol_table_initialized_
+ // will be set to the valid state, but symbol_table_ will be nullptr.
+ void InitializeSymbolTable() const;
+
+ PointerVector<MachOImageSegmentReader> segments_;
+ std::map<std::string, size_t> segment_map_;
+ std::string module_name_;
+ std::string module_info_;
+ std::string dylinker_name_;
+ crashpad::UUID uuid_;
+ mach_vm_address_t address_;
+ mach_vm_size_t size_;
+ mach_vm_size_t slide_;
+ uint64_t source_version_;
+ scoped_ptr<process_types::symtab_command> symtab_command_;
+ scoped_ptr<process_types::dysymtab_command> dysymtab_command_;
+
+ // symbol_table_ (and symbol_table_initialized_) are mutable in order to
+ // maintain LookUpExternalDefinedSymbol() as a const interface while allowing
+ // lazy initialization via InitializeSymbolTable(). This is logical
+ // const-ness, not physical const-ness.
+ mutable scoped_ptr<MachOImageSymbolTableReader> symbol_table_;
+
+ scoped_ptr<process_types::dylib_command> id_dylib_command_;
+ ProcessReader* process_reader_; // weak
+ uint32_t file_type_;
+ InitializationStateDcheck initialized_;
+
+ // symbol_table_initialized_ protects symbol_table_: symbol_table_ can only
+ // be used when symbol_table_initialized_ is valid, although
+ // symbol_table_initialized_ being valid doesn’t imply that symbol_table_ is
+ // set. symbol_table_initialized_ will be valid without symbol_table_ being
+ // set in modules that have no symbol table.
+ mutable InitializationState symbol_table_initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(MachOImageReader);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_READER_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader_test.cc
new file mode 100644
index 00000000000..793290969a0
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader_test.cc
@@ -0,0 +1,644 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/mac/mach_o_image_reader.h"
+
+#include <AvailabilityMacros.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+#include <mach-o/dyld_images.h>
+#include <mach-o/getsect.h>
+#include <mach-o/ldsyms.h>
+#include <mach-o/loader.h>
+#include <mach-o/nlist.h>
+#include <stdint.h>
+
+#include "base/strings/stringprintf.h"
+#include "build/build_config.h"
+#include "client/crashpad_info.h"
+#include "gtest/gtest.h"
+#include "snapshot/mac/mach_o_image_segment_reader.h"
+#include "snapshot/mac/process_reader.h"
+#include "snapshot/mac/process_types.h"
+#include "test/mac/dyld.h"
+#include "util/misc/uuid.h"
+
+// This file is responsible for testing MachOImageReader,
+// MachOImageSegmentReader, and MachOImageSymbolTableReader.
+
+namespace crashpad {
+namespace test {
+namespace {
+
+// Native types and constants, in cases where the 32-bit and 64-bit versions
+// are different.
+#if defined(ARCH_CPU_64_BITS)
+using MachHeader = mach_header_64;
+const uint32_t kMachMagic = MH_MAGIC_64;
+using SegmentCommand = segment_command_64;
+const uint32_t kSegmentCommand = LC_SEGMENT_64;
+using Section = section_64;
+using Nlist = nlist_64;
+#else
+using MachHeader = mach_header;
+const uint32_t kMachMagic = MH_MAGIC;
+using SegmentCommand = segment_command;
+const uint32_t kSegmentCommand = LC_SEGMENT;
+using Section = section;
+
+// This needs to be called “struct nlist” because “nlist” without the struct
+// refers to the nlist() function.
+using Nlist = struct nlist;
+#endif
+
+#if defined(ARCH_CPU_X86_64)
+const int kCPUType = CPU_TYPE_X86_64;
+#elif defined(ARCH_CPU_X86)
+const int kCPUType = CPU_TYPE_X86;
+#endif
+
+// Verifies that |expect_section| and |actual_section| agree.
+void ExpectSection(const Section* expect_section,
+ const process_types::section* actual_section) {
+ ASSERT_TRUE(expect_section);
+ ASSERT_TRUE(actual_section);
+
+ EXPECT_EQ(
+ MachOImageSegmentReader::SectionNameString(expect_section->sectname),
+ MachOImageSegmentReader::SectionNameString(actual_section->sectname));
+ EXPECT_EQ(
+ MachOImageSegmentReader::SegmentNameString(expect_section->segname),
+ MachOImageSegmentReader::SegmentNameString(actual_section->segname));
+ EXPECT_EQ(expect_section->addr, actual_section->addr);
+ EXPECT_EQ(expect_section->size, actual_section->size);
+ EXPECT_EQ(expect_section->offset, actual_section->offset);
+ EXPECT_EQ(expect_section->align, actual_section->align);
+ EXPECT_EQ(expect_section->reloff, actual_section->reloff);
+ EXPECT_EQ(expect_section->nreloc, actual_section->nreloc);
+ EXPECT_EQ(expect_section->flags, actual_section->flags);
+ EXPECT_EQ(expect_section->reserved1, actual_section->reserved1);
+ EXPECT_EQ(expect_section->reserved2, actual_section->reserved2);
+}
+
+// Verifies that |expect_segment| is a valid Mach-O segment load command for the
+// current system by checking its |cmd| field. Then, verifies that the
+// information in |actual_segment| matches that in |expect_segment|. The
+// |segname|, |vmaddr|, |vmsize|, and |fileoff| fields are examined. Each
+// section within the segment is also examined by calling ExpectSection().
+// Access to each section via both MachOImageSegmentReader::GetSectionByName()
+// and MachOImageReader::GetSectionByName() is verified, expecting that each
+// call produces the same section. Segment and section data addresses are
+// verified against data obtained by calling getsegmentdata() and
+// getsectiondata(). The segment is checked to make sure that it behaves
+// correctly when attempting to look up a nonexistent section by name.
+// |section_index| is used to track the last-used section index in an image on
+// entry, and is reset to the last-used section index on return after the
+// sections are processed. This is used to test that
+// MachOImageReader::GetSectionAtIndex() returns the correct result.
+void ExpectSegmentCommand(const SegmentCommand* expect_segment,
+ const MachHeader* expect_image,
+ const MachOImageSegmentReader* actual_segment,
+ const MachOImageReader* actual_image,
+ size_t* section_index) {
+ ASSERT_TRUE(expect_segment);
+ ASSERT_TRUE(actual_segment);
+
+ EXPECT_EQ(kSegmentCommand, expect_segment->cmd);
+
+ std::string segment_name = actual_segment->Name();
+ EXPECT_EQ(MachOImageSegmentReader::SegmentNameString(expect_segment->segname),
+ segment_name);
+ EXPECT_EQ(expect_segment->vmaddr, actual_segment->vmaddr());
+ EXPECT_EQ(expect_segment->vmsize, actual_segment->vmsize());
+ EXPECT_EQ(expect_segment->fileoff, actual_segment->fileoff());
+
+ if (actual_segment->SegmentSlides()) {
+ EXPECT_EQ(actual_segment->Address(),
+ actual_segment->vmaddr() + actual_image->Slide());
+
+ unsigned long expect_segment_size;
+ const uint8_t* expect_segment_data = getsegmentdata(
+ expect_image, segment_name.c_str(), &expect_segment_size);
+ mach_vm_address_t expect_segment_address =
+ reinterpret_cast<mach_vm_address_t>(expect_segment_data);
+ EXPECT_EQ(expect_segment_address, actual_segment->Address());
+ EXPECT_EQ(expect_segment_size, actual_segment->vmsize());
+ EXPECT_EQ(actual_segment->vmsize(), actual_segment->Size());
+ } else {
+ // getsegmentdata() doesn’t return appropriate data for the __PAGEZERO
+ // segment because getsegmentdata() always adjusts for slide, but the
+ // __PAGEZERO segment never slides, it just grows. Skip the getsegmentdata()
+ // check for that segment according to the same rules that the kernel uses
+ // to identify __PAGEZERO. See 10.9.4 xnu-2422.110.17/bsd/kern/mach_loader.c
+ // load_segment().
+ EXPECT_EQ(actual_segment->Address(), actual_segment->vmaddr());
+ EXPECT_EQ(actual_segment->vmsize() + actual_image->Slide(),
+ actual_segment->Size());
+ }
+
+ ASSERT_EQ(expect_segment->nsects, actual_segment->nsects());
+
+ // Make sure that the expected load command is big enough for the number of
+ // sections that it claims to have, and set up a pointer to its first section
+ // structure.
+ ASSERT_EQ(sizeof(*expect_segment) + expect_segment->nsects * sizeof(Section),
+ expect_segment->cmdsize);
+ const Section* expect_sections =
+ reinterpret_cast<const Section*>(&expect_segment[1]);
+
+ for (size_t index = 0; index < actual_segment->nsects(); ++index) {
+ const Section* expect_section = &expect_sections[index];
+ const process_types::section* actual_section =
+ actual_segment->GetSectionAtIndex(index, nullptr);
+ ASSERT_NO_FATAL_FAILURE(
+ ExpectSection(&expect_sections[index], actual_section));
+
+ // Make sure that the section is accessible by GetSectionByName as well.
+ std::string section_name =
+ MachOImageSegmentReader::SectionNameString(expect_section->sectname);
+ const process_types::section* actual_section_by_name =
+ actual_segment->GetSectionByName(section_name, nullptr);
+ EXPECT_EQ(actual_section, actual_section_by_name);
+
+ // Make sure that the section is accessible by the parent MachOImageReader’s
+ // GetSectionByName.
+ mach_vm_address_t actual_section_address;
+ const process_types::section* actual_section_from_image_by_name =
+ actual_image->GetSectionByName(
+ segment_name, section_name, &actual_section_address);
+ EXPECT_EQ(actual_section, actual_section_from_image_by_name);
+
+ if (actual_segment->SegmentSlides()) {
+ EXPECT_EQ(actual_section_address,
+ actual_section->addr + actual_image->Slide());
+
+ unsigned long expect_section_size;
+ const uint8_t* expect_section_data = getsectiondata(expect_image,
+ segment_name.c_str(),
+ section_name.c_str(),
+ &expect_section_size);
+ mach_vm_address_t expect_section_address =
+ reinterpret_cast<mach_vm_address_t>(expect_section_data);
+ EXPECT_EQ(expect_section_address, actual_section_address);
+ EXPECT_EQ(expect_section_size, actual_section->size);
+ } else {
+ EXPECT_EQ(actual_section_address, actual_section->addr);
+ }
+
+ // Test the parent MachOImageReader’s GetSectionAtIndex as well.
+ const MachOImageSegmentReader* containing_segment;
+ mach_vm_address_t actual_section_address_at_index;
+ const process_types::section* actual_section_from_image_at_index =
+ actual_image->GetSectionAtIndex(++(*section_index),
+ &containing_segment,
+ &actual_section_address_at_index);
+ EXPECT_EQ(actual_section, actual_section_from_image_at_index);
+ EXPECT_EQ(actual_segment, containing_segment);
+ EXPECT_EQ(actual_section_address, actual_section_address_at_index);
+ }
+
+ EXPECT_EQ(nullptr,
+ actual_segment->GetSectionByName("NoSuchSection", nullptr));
+}
+
+// Walks through the load commands of |expect_image|, finding all of the
+// expected segment commands. For each expected segment command, calls
+// actual_image->GetSegmentByName() to obtain an actual segment command, and
+// calls ExpectSegmentCommand() to compare the expected and actual segments. A
+// series of by-name lookups is also performed on the segment to ensure that it
+// behaves correctly when attempting to look up segment and section names that
+// are not present. |test_section_indices| should be true to test
+// MachOImageReader::GetSectionAtIndex() using out-of-range section indices.
+// This should be tested for at least one module, but it’s very noisy in terms
+// of logging output, so this knob is provided to suppress this portion of the
+// test when looping over all modules.
+void ExpectSegmentCommands(const MachHeader* expect_image,
+ const MachOImageReader* actual_image,
+ bool test_section_index_bounds) {
+ ASSERT_TRUE(expect_image);
+ ASSERT_TRUE(actual_image);
+
+ // &expect_image[1] points right past the end of the mach_header[_64], to the
+ // start of the load commands.
+ const char* commands_base = reinterpret_cast<const char*>(&expect_image[1]);
+ uint32_t position = 0;
+ size_t section_index = 0;
+ for (uint32_t index = 0; index < expect_image->ncmds; ++index) {
+ ASSERT_LT(position, expect_image->sizeofcmds);
+ const load_command* command =
+ reinterpret_cast<const load_command*>(&commands_base[position]);
+ ASSERT_LE(position + command->cmdsize, expect_image->sizeofcmds);
+ if (command->cmd == kSegmentCommand) {
+ ASSERT_GE(command->cmdsize, sizeof(SegmentCommand));
+ const SegmentCommand* expect_segment =
+ reinterpret_cast<const SegmentCommand*>(command);
+ std::string segment_name =
+ MachOImageSegmentReader::SegmentNameString(expect_segment->segname);
+ const MachOImageSegmentReader* actual_segment =
+ actual_image->GetSegmentByName(segment_name);
+ ASSERT_NO_FATAL_FAILURE(ExpectSegmentCommand(expect_segment,
+ expect_image,
+ actual_segment,
+ actual_image,
+ &section_index));
+ }
+ position += command->cmdsize;
+ }
+ EXPECT_EQ(expect_image->sizeofcmds, position);
+
+ if (test_section_index_bounds) {
+ // GetSectionAtIndex uses a 1-based index. Make sure that the range is
+ // correct.
+ EXPECT_EQ(nullptr, actual_image->GetSectionAtIndex(0, nullptr, nullptr));
+ EXPECT_EQ(
+ nullptr,
+ actual_image->GetSectionAtIndex(section_index + 1, nullptr, nullptr));
+ }
+
+ // Make sure that by-name lookups for names that don’t exist work properly:
+ // they should return nullptr.
+ EXPECT_FALSE(actual_image->GetSegmentByName("NoSuchSegment"));
+ EXPECT_FALSE(actual_image->GetSectionByName(
+ "NoSuchSegment", "NoSuchSection", nullptr));
+
+ // Make sure that there’s a __TEXT segment so that this can do a valid test of
+ // a section that doesn’t exist within a segment that does.
+ EXPECT_TRUE(actual_image->GetSegmentByName(SEG_TEXT));
+ EXPECT_FALSE(
+ actual_image->GetSectionByName(SEG_TEXT, "NoSuchSection", nullptr));
+
+ // Similarly, make sure that a section name that exists in one segment isn’t
+ // accidentally found during a lookup for that section in a different segment.
+ //
+ // If the image has no sections (unexpected), then any section lookup should
+ // fail, and these initial values of test_segment and test_section are fine
+ // for the EXPECT_FALSE checks on GetSectionByName() below.
+ std::string test_segment = SEG_DATA;
+ std::string test_section = SECT_TEXT;
+
+ const process_types::section* section =
+ actual_image->GetSectionAtIndex(1, nullptr, nullptr);
+ if (section) {
+ // Use the name of the first section in the image as the section that
+ // shouldn’t appear in a different segment. If the first section is in the
+ // __TEXT segment (as it is normally), then a section by the same name
+ // wouldn’t be expected in the __DATA segment. But if the first section is
+ // in any other segment, then it wouldn’t be expected in the __TEXT segment.
+ if (MachOImageSegmentReader::SegmentNameString(section->segname) ==
+ SEG_TEXT) {
+ test_segment = SEG_DATA;
+ } else {
+ test_segment = SEG_TEXT;
+ }
+ test_section =
+ MachOImageSegmentReader::SectionNameString(section->sectname);
+
+ // It should be possible to look up the first section by name.
+ EXPECT_EQ(section,
+ actual_image->GetSectionByName(
+ section->segname, section->sectname, nullptr));
+ }
+ EXPECT_FALSE(
+ actual_image->GetSectionByName("NoSuchSegment", test_section, nullptr));
+ EXPECT_FALSE(
+ actual_image->GetSectionByName(test_segment, test_section, nullptr));
+
+ // The __LINKEDIT segment normally does exist but doesn’t have any sections.
+ EXPECT_FALSE(
+ actual_image->GetSectionByName(SEG_LINKEDIT, "NoSuchSection", nullptr));
+ EXPECT_FALSE(
+ actual_image->GetSectionByName(SEG_LINKEDIT, SECT_TEXT, nullptr));
+}
+
+// In some cases, the expected slide value for an image is unknown, because no
+// reasonable API to return it is provided. When this happens, use kSlideUnknown
+// to avoid checking the actual slide value against anything.
+const mach_vm_size_t kSlideUnknown = std::numeric_limits<mach_vm_size_t>::max();
+
+// Verifies that |expect_image| is a vaild Mach-O header for the current system
+// by checking its |magic| and |cputype| fields. Then, verifies that the
+// information in |actual_image| matches that in |expect_image|. The |filetype|
+// field is examined, actual_image->Address() is compared to
+// |expect_image_address|, and actual_image->Slide() is compared to
+// |expect_image_slide|, unless |expect_image_slide| is kSlideUnknown. Various
+// other attributes of |actual_image| are sanity-checked depending on the Mach-O
+// file type. Finally, ExpectSegmentCommands() is called to verify all that all
+// of the segments match; |test_section_index_bounds| is used as an argument to
+// that function.
+void ExpectMachImage(const MachHeader* expect_image,
+ mach_vm_address_t expect_image_address,
+ mach_vm_size_t expect_image_slide,
+ const MachOImageReader* actual_image,
+ bool test_section_index_bounds) {
+ ASSERT_TRUE(expect_image);
+ ASSERT_TRUE(actual_image);
+
+ EXPECT_EQ(kMachMagic, expect_image->magic);
+ EXPECT_EQ(kCPUType, expect_image->cputype);
+
+ EXPECT_EQ(expect_image->filetype, actual_image->FileType());
+ EXPECT_EQ(expect_image_address, actual_image->Address());
+ if (expect_image_slide != kSlideUnknown) {
+ EXPECT_EQ(expect_image_slide, actual_image->Slide());
+ }
+
+ const MachOImageSegmentReader* actual_text_segment =
+ actual_image->GetSegmentByName(SEG_TEXT);
+ ASSERT_TRUE(actual_text_segment);
+ EXPECT_EQ(expect_image_address, actual_text_segment->Address());
+ EXPECT_EQ(actual_image->Size(), actual_text_segment->Size());
+ EXPECT_EQ(expect_image_address - actual_text_segment->vmaddr(),
+ actual_image->Slide());
+
+ uint32_t file_type = actual_image->FileType();
+ EXPECT_TRUE(file_type == MH_EXECUTE || file_type == MH_DYLIB ||
+ file_type == MH_DYLINKER || file_type == MH_BUNDLE);
+
+ if (file_type == MH_EXECUTE || file_type == MH_DYLINKER) {
+ EXPECT_EQ("/usr/lib/dyld", actual_image->DylinkerName());
+ }
+
+ // For these, just don’t crash or anything.
+ if (file_type == MH_DYLIB) {
+ actual_image->DylibVersion();
+ }
+ actual_image->SourceVersion();
+ UUID uuid;
+ actual_image->UUID(&uuid);
+
+ ASSERT_NO_FATAL_FAILURE(ExpectSegmentCommands(
+ expect_image, actual_image, test_section_index_bounds));
+}
+
+// Verifies the symbol whose Nlist structure is |entry| and whose name is |name|
+// matches the value of a symbol by the same name looked up in |actual_image|.
+// MachOImageReader::LookUpExternalDefinedSymbol() is used for this purpose.
+// Only external defined symbols are considered, other types of symbols are
+// excluded because LookUpExternalDefinedSymbol() only deals with external
+// defined symbols.
+void ExpectSymbol(const Nlist* entry,
+ const char* name,
+ const MachOImageReader* actual_image) {
+ SCOPED_TRACE(name);
+
+ uint32_t entry_type = entry->n_type & N_TYPE;
+ if ((entry->n_type & N_STAB) == 0 && (entry->n_type & N_PEXT) == 0 &&
+ (entry_type == N_ABS || entry_type == N_SECT) &&
+ (entry->n_type & N_EXT) == 1) {
+ mach_vm_address_t actual_address;
+ ASSERT_TRUE(
+ actual_image->LookUpExternalDefinedSymbol(name, &actual_address));
+
+ // Since the nlist interface was used to read the symbol, use it to compute
+ // the symbol address too. This isn’t perfect, and it should be possible in
+ // theory to use dlsym() to get the expected address of a symbol. In
+ // practice, dlsym() is difficult to use when only a MachHeader* is
+ // available as in this function, as opposed to a void* opaque handle. It is
+ // possible to get a void* handle by using dladdr() to find the file name
+ // corresponding to the MachHeader*, and using dlopen() again on that name,
+ // assuming it hasn’t changed on disk since being loaded. However, even with
+ // that being done, dlsym() can only deal with symbols whose names begin
+ // with an underscore (and requires that the leading underscore be trimmed).
+ // dlsym() will also return different addresses for symbols that are
+ // resolved via symbol resolver.
+ mach_vm_address_t expect_address = entry->n_value;
+ if (entry_type == N_SECT) {
+ EXPECT_GE(entry->n_sect, 1u);
+ expect_address += actual_image->Slide();
+ } else {
+ EXPECT_EQ(NO_SECT, entry->n_sect);
+ }
+
+ EXPECT_EQ(expect_address, actual_address);
+ }
+
+ // You’d think that it might be a good idea to verify that if the conditions
+ // above weren’t met, that the symbol didn’t show up in actual_image’s symbol
+ // table at all. Unfortunately, it’s possible for the same name to show up as
+ // both an external defined symbol and as something else, so it’s not possible
+ // to verify this reliably.
+}
+
+// Locates the symbol table in |expect_image| and verifies that all of the
+// external defined symbols found there are also present and have the same
+// values in |actual_image|. ExpectSymbol() is used to verify the actual symbol.
+void ExpectSymbolTable(const MachHeader* expect_image,
+ const MachOImageReader* actual_image) {
+ // This intentionally consults only LC_SYMTAB and not LC_DYSYMTAB so that it
+ // can look at the larger set of all symbols. The actual implementation being
+ // tested is free to consult LC_DYSYMTAB, but that’s considered an
+ // optimization. It’s not necessary for the test, and it’s better for the test
+ // to expose bugs in that optimization rather than duplicate them.
+ const char* commands_base = reinterpret_cast<const char*>(&expect_image[1]);
+ uint32_t position = 0;
+ const symtab_command* symtab = nullptr;
+ const SegmentCommand* linkedit = nullptr;
+ for (uint32_t index = 0; index < expect_image->ncmds; ++index) {
+ ASSERT_LT(position, expect_image->sizeofcmds);
+ const load_command* command =
+ reinterpret_cast<const load_command*>(&commands_base[position]);
+ ASSERT_LE(position + command->cmdsize, expect_image->sizeofcmds);
+ if (command->cmd == LC_SYMTAB) {
+ ASSERT_FALSE(symtab);
+ ASSERT_EQ(sizeof(symtab_command), command->cmdsize);
+ symtab = reinterpret_cast<const symtab_command*>(command);
+ } else if (command->cmd == kSegmentCommand) {
+ ASSERT_GE(command->cmdsize, sizeof(SegmentCommand));
+ const SegmentCommand* segment =
+ reinterpret_cast<const SegmentCommand*>(command);
+ std::string segment_name =
+ MachOImageSegmentReader::SegmentNameString(segment->segname);
+ if (segment_name == SEG_LINKEDIT) {
+ ASSERT_FALSE(linkedit);
+ linkedit = segment;
+ }
+ }
+ position += command->cmdsize;
+ }
+
+ if (symtab) {
+ ASSERT_TRUE(linkedit);
+
+ const char* linkedit_base =
+ reinterpret_cast<const char*>(linkedit->vmaddr + actual_image->Slide());
+ const Nlist* nlist = reinterpret_cast<const Nlist*>(
+ linkedit_base + symtab->symoff - linkedit->fileoff);
+ const char* strtab = linkedit_base + symtab->stroff - linkedit->fileoff;
+
+ for (uint32_t index = 0; index < symtab->nsyms; ++index) {
+ const Nlist* entry = nlist + index;
+ const char* name = strtab + entry->n_un.n_strx;
+ ASSERT_NO_FATAL_FAILURE(ExpectSymbol(entry, name, actual_image));
+ }
+ }
+
+ mach_vm_address_t ignore;
+ EXPECT_FALSE(actual_image->LookUpExternalDefinedSymbol("", &ignore));
+ EXPECT_FALSE(
+ actual_image->LookUpExternalDefinedSymbol("NoSuchSymbolName", &ignore));
+ EXPECT_FALSE(
+ actual_image->LookUpExternalDefinedSymbol("_NoSuchSymbolName", &ignore));
+}
+
+TEST(MachOImageReader, Self_MainExecutable) {
+ ProcessReader process_reader;
+ ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
+
+ const MachHeader* mh_execute_header =
+ reinterpret_cast<MachHeader*>(dlsym(RTLD_MAIN_ONLY, MH_EXECUTE_SYM));
+ ASSERT_NE(nullptr, mh_execute_header);
+ mach_vm_address_t mh_execute_header_address =
+ reinterpret_cast<mach_vm_address_t>(mh_execute_header);
+
+ MachOImageReader image_reader;
+ ASSERT_TRUE(image_reader.Initialize(
+ &process_reader, mh_execute_header_address, "executable"));
+
+ EXPECT_EQ(implicit_cast<uint32_t>(MH_EXECUTE), image_reader.FileType());
+
+ // The main executable has image index 0.
+ intptr_t image_slide = _dyld_get_image_vmaddr_slide(0);
+
+ ASSERT_NO_FATAL_FAILURE(ExpectMachImage(mh_execute_header,
+ mh_execute_header_address,
+ image_slide,
+ &image_reader,
+ true));
+
+ // This symbol, __mh_execute_header, is known to exist in all MH_EXECUTE
+ // Mach-O files.
+ mach_vm_address_t symbol_address;
+ ASSERT_TRUE(image_reader.LookUpExternalDefinedSymbol(_MH_EXECUTE_SYM,
+ &symbol_address));
+ EXPECT_EQ(mh_execute_header_address, symbol_address);
+
+ ASSERT_NO_FATAL_FAILURE(ExpectSymbolTable(mh_execute_header, &image_reader));
+}
+
+TEST(MachOImageReader, Self_DyldImages) {
+ ProcessReader process_reader;
+ ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
+
+ uint32_t count = _dyld_image_count();
+ ASSERT_GE(count, 1u);
+
+ size_t modules_with_crashpad_info = 0;
+
+ for (uint32_t index = 0; index < count; ++index) {
+ const char* image_name = _dyld_get_image_name(index);
+ SCOPED_TRACE(base::StringPrintf("index %u, image %s", index, image_name));
+
+ // _dyld_get_image_header() is poorly-declared: it’s declared as returning
+ // const mach_header* in both 32-bit and 64-bit environments, but in the
+ // 64-bit environment, it should be const mach_header_64*.
+ const MachHeader* mach_header =
+ reinterpret_cast<const MachHeader*>(_dyld_get_image_header(index));
+ mach_vm_address_t image_address =
+ reinterpret_cast<mach_vm_address_t>(mach_header);
+
+ MachOImageReader image_reader;
+ ASSERT_TRUE(
+ image_reader.Initialize(&process_reader, image_address, image_name));
+
+ uint32_t file_type = image_reader.FileType();
+ if (index == 0) {
+ EXPECT_EQ(implicit_cast<uint32_t>(MH_EXECUTE), file_type);
+ } else {
+ EXPECT_TRUE(file_type == MH_DYLIB || file_type == MH_BUNDLE);
+ }
+
+ intptr_t image_slide = _dyld_get_image_vmaddr_slide(index);
+ ASSERT_NO_FATAL_FAILURE(ExpectMachImage(
+ mach_header, image_address, image_slide, &image_reader, false));
+
+ ASSERT_NO_FATAL_FAILURE(ExpectSymbolTable(mach_header, &image_reader));
+
+ process_types::CrashpadInfo crashpad_info;
+ if (image_reader.GetCrashpadInfo(&crashpad_info)) {
+ ++modules_with_crashpad_info;
+ }
+ }
+
+ EXPECT_GE(modules_with_crashpad_info, 1u);
+
+ // Now that all of the modules have been verified, make sure that dyld itself
+ // can be read properly too.
+ const struct dyld_all_image_infos* dyld_image_infos =
+ _dyld_get_all_image_infos();
+ ASSERT_GE(dyld_image_infos->version, 1u);
+ EXPECT_EQ(count, dyld_image_infos->infoArrayCount);
+
+ if (dyld_image_infos->version >= 2) {
+ SCOPED_TRACE("dyld");
+
+ // dyld_all_image_infos::dyldImageLoadAddress is poorly-declared too.
+ const MachHeader* mach_header = reinterpret_cast<const MachHeader*>(
+ dyld_image_infos->dyldImageLoadAddress);
+ mach_vm_address_t image_address =
+ reinterpret_cast<mach_vm_address_t>(mach_header);
+
+ MachOImageReader image_reader;
+ ASSERT_TRUE(
+ image_reader.Initialize(&process_reader, image_address, "dyld"));
+
+ EXPECT_EQ(implicit_cast<uint32_t>(MH_DYLINKER), image_reader.FileType());
+
+ // There’s no good API to get dyld’s slide, so don’t bother checking it.
+ ASSERT_NO_FATAL_FAILURE(ExpectMachImage(
+ mach_header, image_address, kSlideUnknown, &image_reader, false));
+
+ ASSERT_NO_FATAL_FAILURE(ExpectSymbolTable(mach_header, &image_reader));
+ }
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
+ // If dyld is new enough to record UUIDs, check the UUID of any module that
+ // it says has one. Note that dyld doesn’t record UUIDs of anything that
+ // loaded out of the shared cache, but it should at least have a UUID for the
+ // main executable if it has one.
+ if (dyld_image_infos->version >= 8 && dyld_image_infos->uuidArray) {
+ for (uint32_t index = 0;
+ index < dyld_image_infos->uuidArrayCount;
+ ++index) {
+ const dyld_uuid_info* dyld_image = &dyld_image_infos->uuidArray[index];
+ SCOPED_TRACE(base::StringPrintf("uuid index %u", index));
+
+ // dyld_uuid_info::imageLoadAddress is poorly-declared too.
+ const MachHeader* mach_header =
+ reinterpret_cast<const MachHeader*>(dyld_image->imageLoadAddress);
+ mach_vm_address_t image_address =
+ reinterpret_cast<mach_vm_address_t>(mach_header);
+
+ MachOImageReader image_reader;
+ ASSERT_TRUE(
+ image_reader.Initialize(&process_reader, image_address, "uuid"));
+
+ // There’s no good way to get the image’s slide here, although the image
+ // should have already been checked along with its slide above, in the
+ // loop through all images.
+ ExpectMachImage(
+ mach_header, image_address, kSlideUnknown, &image_reader, false);
+
+ UUID expected_uuid;
+ expected_uuid.InitializeFromBytes(dyld_image->imageUUID);
+ UUID actual_uuid;
+ image_reader.UUID(&actual_uuid);
+ EXPECT_EQ(expected_uuid, actual_uuid);
+ }
+ }
+#endif
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader.cc
new file mode 100644
index 00000000000..8a077ddec03
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader.cc
@@ -0,0 +1,301 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/mac/mach_o_image_segment_reader.h"
+
+#include <mach-o/loader.h>
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "snapshot/mac/process_reader.h"
+#include "util/mac/checked_mach_address_range.h"
+#include "util/mac/mac_util.h"
+#include "util/stdlib/strnlen.h"
+
+namespace crashpad {
+
+namespace {
+
+std::string SizeLimitedCString(const char* c_string, size_t max_length) {
+ return std::string(c_string, strnlen(c_string, max_length));
+}
+
+} // namespace
+
+MachOImageSegmentReader::MachOImageSegmentReader()
+ : segment_command_(),
+ sections_(),
+ section_map_(),
+ slide_(0),
+ initialized_(),
+ initialized_slide_() {
+}
+
+MachOImageSegmentReader::~MachOImageSegmentReader() {
+}
+
+bool MachOImageSegmentReader::Initialize(ProcessReader* process_reader,
+ mach_vm_address_t load_command_address,
+ const std::string& load_command_info,
+ const std::string& module_name,
+ uint32_t file_type) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ if (!segment_command_.Read(process_reader, load_command_address)) {
+ LOG(WARNING) << "could not read segment_command" << load_command_info;
+ return false;
+ }
+
+ const uint32_t kExpectedSegmentCommand =
+ process_reader->Is64Bit() ? LC_SEGMENT_64 : LC_SEGMENT;
+ DCHECK_EQ(segment_command_.cmd, kExpectedSegmentCommand);
+ DCHECK_GE(segment_command_.cmdsize, segment_command_.Size());
+ const size_t kSectionStructSize =
+ process_types::section::ExpectedSize(process_reader);
+ const size_t kRequiredSize =
+ segment_command_.Size() + segment_command_.nsects * kSectionStructSize;
+ if (segment_command_.cmdsize < kRequiredSize) {
+ LOG(WARNING) << base::StringPrintf(
+ "segment command cmdsize 0x%x insufficient for %u "
+ "section%s (0x%zx)",
+ segment_command_.cmdsize,
+ segment_command_.nsects,
+ segment_command_.nsects == 1 ? "" : "s",
+ kRequiredSize) << load_command_info;
+ return false;
+ }
+
+ std::string segment_name = NameInternal();
+ std::string segment_info = base::StringPrintf(
+ ", segment %s%s", segment_name.c_str(), load_command_info.c_str());
+
+ // This checks the unslid segment range. The slid range (as loaded into
+ // memory) will be checked later by MachOImageReader.
+ CheckedMachAddressRange segment_range(process_reader->Is64Bit(),
+ segment_command_.vmaddr,
+ segment_command_.vmsize);
+ if (!segment_range.IsValid()) {
+ LOG(WARNING) << base::StringPrintf("invalid segment range 0x%llx + 0x%llx",
+ segment_command_.vmaddr,
+ segment_command_.vmsize) << segment_info;
+ return false;
+ }
+
+ sections_.resize(segment_command_.nsects);
+ if (!process_types::section::ReadArrayInto(
+ process_reader,
+ load_command_address + segment_command_.Size(),
+ segment_command_.nsects,
+ &sections_[0])) {
+ LOG(WARNING) << "could not read sections" << segment_info;
+ return false;
+ }
+
+ for (size_t section_index = 0;
+ section_index < sections_.size();
+ ++section_index) {
+ const process_types::section& section = sections_[section_index];
+ std::string section_segment_name = SegmentNameString(section.segname);
+ std::string section_name = SectionNameString(section.sectname);
+ std::string section_full_name =
+ SegmentAndSectionNameString(section.segname, section.sectname);
+
+ std::string section_info = base::StringPrintf(", section %s %zu/%zu%s",
+ section_full_name.c_str(),
+ section_index,
+ sections_.size(),
+ load_command_info.c_str());
+
+ if (section_segment_name != segment_name) {
+ // cl_kernels modules (for OpenCL) aren’t ld output, and they’re formatted
+ // incorrectly on Mac OS X 10.10. They have a single __TEXT segment, but
+ // one of the sections within it claims to belong to the __LD segment.
+ // This mismatch shouldn’t happen. This errant section also has the
+ // S_ATTR_DEBUG flag set, which shouldn’t happen unless all of the other
+ // sections in the segment also have this bit set (they don’t). These odd
+ // sections are reminiscent of unwind information stored in MH_OBJECT
+ // images, although cl_kernels images claim to be MH_BUNDLE. Because at
+ // least one cl_kernels module will commonly be found in a process, and
+ // sometimes more will be, tolerate this quirk.
+ //
+ // https://openradar.appspot.com/20239912
+ if (!(file_type == MH_BUNDLE &&
+ module_name == "cl_kernels" &&
+ MacOSXMinorVersion() == 10 &&
+ segment_name == SEG_TEXT &&
+ section_segment_name == "__LD" &&
+ section_name == "__compact_unwind" &&
+ (section.flags & S_ATTR_DEBUG))) {
+ LOG(WARNING) << "section.segname incorrect in segment " << segment_name
+ << section_info;
+ return false;
+ }
+ }
+
+ CheckedMachAddressRange section_range(
+ process_reader->Is64Bit(), section.addr, section.size);
+ if (!section_range.IsValid()) {
+ LOG(WARNING) << base::StringPrintf(
+ "invalid section range 0x%llx + 0x%llx",
+ section.addr,
+ section.size) << section_info;
+ return false;
+ }
+
+ if (!segment_range.ContainsRange(section_range)) {
+ LOG(WARNING) << base::StringPrintf(
+ "section at 0x%llx + 0x%llx outside of segment at "
+ "0x%llx + 0x%llx",
+ section.addr,
+ section.size,
+ segment_command_.vmaddr,
+ segment_command_.vmsize) << section_info;
+ return false;
+ }
+
+ uint32_t section_type = (section.flags & SECTION_TYPE);
+ bool zero_fill = section_type == S_ZEROFILL ||
+ section_type == S_GB_ZEROFILL ||
+ section_type == S_THREAD_LOCAL_ZEROFILL;
+
+ // Zero-fill section types aren’t mapped from the file, so their |offset|
+ // fields are irrelevant and are typically 0.
+ if (!zero_fill &&
+ section.offset - segment_command_.fileoff !=
+ section.addr - segment_command_.vmaddr) {
+ LOG(WARNING) << base::StringPrintf(
+ "section type 0x%x at 0x%llx has unexpected offset "
+ "0x%x in segment at 0x%llx with offset 0x%llx",
+ section_type,
+ section.addr,
+ section.offset,
+ segment_command_.vmaddr,
+ segment_command_.fileoff) << section_info;
+ return false;
+ }
+
+ const auto insert_result =
+ section_map_.insert(std::make_pair(section_name, section_index));
+ if (!insert_result.second) {
+ LOG(WARNING) << base::StringPrintf("duplicate section name at %zu",
+ insert_result.first->second)
+ << section_info;
+ return false;
+ }
+ }
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+std::string MachOImageSegmentReader::Name() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return NameInternal();
+}
+
+mach_vm_address_t MachOImageSegmentReader::Address() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_slide_);
+ return vmaddr() + (SegmentSlides() ? slide_ : 0);
+}
+
+mach_vm_size_t MachOImageSegmentReader::Size() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_slide_);
+ return vmsize() + (SegmentSlides() ? 0 : slide_);
+}
+
+const process_types::section* MachOImageSegmentReader::GetSectionByName(
+ const std::string& section_name,
+ mach_vm_address_t* address) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ const auto& iterator = section_map_.find(section_name);
+ if (iterator == section_map_.end()) {
+ return nullptr;
+ }
+
+ return GetSectionAtIndex(iterator->second, address);
+}
+
+const process_types::section* MachOImageSegmentReader::GetSectionAtIndex(
+ size_t index,
+ mach_vm_address_t* address) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ CHECK_LT(index, sections_.size());
+
+ const process_types::section* section = &sections_[index];
+
+ if (address) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_slide_);
+ *address = section->addr + (SegmentSlides() ? slide_ : 0);
+ }
+
+ return section;
+}
+
+bool MachOImageSegmentReader::SegmentSlides() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ // These are the same rules that the kernel uses to identify __PAGEZERO. See
+ // 10.9.4 xnu-2422.110.17/bsd/kern/mach_loader.c load_segment().
+ return !(segment_command_.vmaddr == 0 && segment_command_.filesize == 0 &&
+ segment_command_.vmsize != 0 &&
+ (segment_command_.initprot & VM_PROT_ALL) == VM_PROT_NONE &&
+ (segment_command_.maxprot & VM_PROT_ALL) == VM_PROT_NONE);
+}
+
+// static
+std::string MachOImageSegmentReader::SegmentNameString(
+ const char* segment_name_c) {
+ // This is used to interpret the segname field of both the segment_command and
+ // section structures, so be sure that they’re identical.
+ static_assert(sizeof(process_types::segment_command::segname) ==
+ sizeof(process_types::section::segname),
+ "sizes must be equal");
+
+ return SizeLimitedCString(segment_name_c,
+ sizeof(process_types::segment_command::segname));
+}
+
+// static
+std::string MachOImageSegmentReader::SectionNameString(
+ const char* section_name_c) {
+ return SizeLimitedCString(section_name_c,
+ sizeof(process_types::section::sectname));
+}
+
+// static
+std::string MachOImageSegmentReader::SegmentAndSectionNameString(
+ const char* segment_name_c,
+ const char* section_name_c) {
+ return base::StringPrintf("%s,%s",
+ SegmentNameString(segment_name_c).c_str(),
+ SectionNameString(section_name_c).c_str());
+}
+
+std::string MachOImageSegmentReader::NameInternal() const {
+ return SegmentNameString(segment_command_.segname);
+}
+
+void MachOImageSegmentReader::SetSlide(mach_vm_size_t slide) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_slide_);
+ slide_ = slide;
+ INITIALIZATION_STATE_SET_VALID(initialized_slide_);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader.h
new file mode 100644
index 00000000000..4e6a97e36cf
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader.h
@@ -0,0 +1,262 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SEGMENT_READER_H_
+#define CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SEGMENT_READER_H_
+
+#include <mach/mach.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "snapshot/mac/process_types.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+
+//! \brief A reader for `LC_SEGMENT` or `LC_SEGMENT_64` load commands in Mach-O
+//! images mapped into another process.
+//!
+//! This class is capable of reading both `LC_SEGMENT` and `LC_SEGMENT_64` based
+//! on the bitness of the remote process.
+//!
+//! A MachOImageSegmentReader will normally be instantiated by a
+//! MachOImageReader.
+class MachOImageSegmentReader {
+ public:
+ MachOImageSegmentReader();
+ ~MachOImageSegmentReader();
+
+ //! \brief Reads the segment load command from another process.
+ //!
+ //! This method must only be called once on an object. This method must be
+ //! called successfully before any other method in this class may be called.
+ //!
+ //! \param[in] process_reader The reader for the remote process.
+ //! \param[in] load_command_address The address, in the remote process’
+ //! address space, where the `LC_SEGMENT` or `LC_SEGMENT_64` load command
+ //! to be read is located. This address is determined by a Mach-O image
+ //! reader, such as MachOImageReader, as it walks Mach-O load commands.
+ //! \param[in] load_command_info A string to be used in logged messages. This
+ //! string is for diagnostic purposes only, and may be empty.
+ //! \param[in] module_name The path used to load the module. This string is
+ //! used to relax otherwise strict parsing rules for common modules with
+ //! known defects.
+ //! \param[in] file_type The module’s Mach-O file type. This is used to relax
+ //! otherwise strict parsing rules for common modules with known defects.
+ //!
+ //! \return `true` if the load command was read successfully. `false`
+ //! otherwise, with an appropriate message logged.
+ bool Initialize(ProcessReader* process_reader,
+ mach_vm_address_t load_command_address,
+ const std::string& load_command_info,
+ const std::string& module_name,
+ uint32_t file_type);
+
+ //! \brief Sets the image’s slide value.
+ //!
+ //! This method must only be called once on an object, after Initialize() is
+ //! called successfully. It must be called before Address(), Size(),
+ //! GetSectionByName(), or GetSectionAtIndex() can be called.
+ //!
+ //! This method is provided because slide is a property of the image that
+ //! cannot be determined until at least some segments have been read. As such,
+ //! it is not necessarily known at the time that Initialize() is called.
+ void SetSlide(mach_vm_size_t slide);
+
+ //! \brief Returns the segment’s name.
+ //!
+ //! The segment’s name is taken from the load command’s `segname` field.
+ //! Common segment names are `"__TEXT"`, `"__DATA"`, and `"__LINKEDIT"`.
+ //! Symbolic constants for these common names are defined in
+ //! `<mach-o/loader.h>`.
+ std::string Name() const;
+
+ //! \return The segment’s actual load address in memory, adjusted for any
+ //! “slide”.
+ //!
+ //! \note For the segment’s preferred load address, not adjusted for slide,
+ //! use vmaddr().
+ mach_vm_address_t Address() const;
+
+ //! \return The segment’s actual size address in memory, adjusted for any
+ //! growth in the case of a nonsliding segment.
+ //!
+ //! \note For the segment’s preferred size, not adjusted for growth, use
+ //! vmsize().
+ mach_vm_address_t Size() const;
+
+ //! \brief The segment’s preferred load address.
+ //!
+ //! \return The segment’s preferred load address as stored in the Mach-O file.
+ //!
+ //! \note This value is not adjusted for any “slide” that may have occurred
+ //! when the image was loaded. Use Address() for a value adjusted for
+ //! slide.
+ //!
+ //! \sa MachOImageReader::GetSegmentByName()
+ mach_vm_address_t vmaddr() const { return segment_command_.vmaddr; }
+
+ //! \brief Returns the segment’s size as mapped into memory.
+ //!
+ //! \note For non-sliding segments, this value is not adjusted for any growth
+ //! that may have occurred when the image was loaded. Use Size() for a
+ //! value adjusted for growth.
+ mach_vm_size_t vmsize() const { return segment_command_.vmsize; }
+
+ //! \brief Returns the file offset of the mapped segment in the file from
+ //! which it was mapped.
+ //!
+ //! The file offset is the difference between the beginning of the
+ //! `mach_header` or `mach_header_64` and the beginning of the segment’s
+ //! mapped region. For segments that are not mapped from a file (such as
+ //! `__PAGEZERO` segments), this will be `0`.
+ mach_vm_size_t fileoff() const { return segment_command_.fileoff; }
+
+ //! \brief Returns the number of sections in the segment.
+ //!
+ //! This will return `0` for a segment without any sections, typical for
+ //! `__PAGEZERO` and `__LINKEDIT` segments.
+ //!
+ //! Although the Mach-O file format uses a `uint32_t` for this field, there is
+ //! an overall limit of 255 sections in an entire Mach-O image file (not just
+ //! in a single segment) imposed by the symbol table format. Symbols will not
+ //! be able to reference anything in a section beyond the first 255 in a
+ //! Mach-O image file.
+ uint32_t nsects() const { return segment_command_.nsects; }
+
+ //! \brief Obtain section information by section name.
+ //!
+ //! \param[in] section_name The name of the section to search for, without the
+ //! leading segment name. For example, use `"__text"`, not
+ //! `"__TEXT,__text"` or `"__TEXT.__text"`.
+ //! \param[out] address The actual address that the section was loaded at in
+ //! memory, taking any “slide” into account if the section did not load at
+ //! its preferred address as stored in the Mach-O image file. This
+ //! parameter can be `nullptr`.
+ //!
+ //! \return A pointer to the section information if it was found, or `nullptr`
+ //! if it was not found. The caller does not take ownership; the lifetime
+ //! of the returned object is scoped to the lifetime of this
+ //! MachOImageSegmentReader object.
+ //!
+ //! \note The process_types::section::addr field gives the section’s preferred
+ //! load address as stored in the Mach-O image file, and is not adjusted
+ //! for any “slide” that may have occurred when the image was loaded.
+ //!
+ //! \sa MachOImageReader::GetSectionByName()
+ const process_types::section* GetSectionByName(
+ const std::string& section_name,
+ mach_vm_address_t* address) const;
+
+ //! \brief Obtain section information by section index.
+ //!
+ //! \param[in] index The index of the section to return, in the order that it
+ //! appears in the segment load command. Unlike
+ //! MachOImageReader::GetSectionAtIndex(), this is a 0-based index. This
+ //! parameter must be in the range of valid indices aas reported by
+ //! nsects().
+ //! \param[out] address The actual address that the section was loaded at in
+ //! memory, taking any “slide” into account if the section did not load at
+ //! its preferred address as stored in the Mach-O image file. This
+ //! parameter can be `nullptr`.
+ //!
+ //! \return A pointer to the section information. If \a index is out of range,
+ //! execution is aborted. The caller does not take ownership; the
+ //! lifetime of the returned object is scoped to the lifetime of this
+ //! MachOImageSegmentReader object.
+ //!
+ //! \note The process_types::section::addr field gives the section’s preferred
+ //! load address as stored in the Mach-O image file, and is not adjusted
+ //! for any “slide” that may have occurred when the image was loaded.
+ //! \note Unlike MachOImageReader::GetSectionAtIndex(), this method does not
+ //! accept out-of-range values for \a index, and aborts execution instead
+ //! of returning `nullptr` upon encountering an out-of-range value. This
+ //! is because this method is expected to be used in a loop that can be
+ //! limited to nsects() iterations, so an out-of-range error can be
+ //! treated more harshly as a logic error, as opposed to a data error.
+ //!
+ //! \sa MachOImageReader::GetSectionAtIndex()
+ const process_types::section* GetSectionAtIndex(
+ size_t index,
+ mach_vm_address_t* address) const;
+
+ //! Returns whether the segment slides.
+ //!
+ //! Most segments slide, but the `__PAGEZERO` segment does not, it grows
+ //! instead. This method identifies non-sliding segments in the same way that
+ //! the kernel does.
+ bool SegmentSlides() const;
+
+ //! \brief Returns a segment name string.
+ //!
+ //! Segment names may be 16 characters long, and are not necessarily
+ //! `NUL`-terminated. This function will return a segment name based on up to
+ //! the first 16 characters found at \a segment_name_c.
+ static std::string SegmentNameString(const char* segment_name_c);
+
+ //! \brief Returns a section name string.
+ //!
+ //! Section names may be 16 characters long, and are not necessarily
+ //! `NUL`-terminated. This function will return a section name based on up to
+ //! the first 16 characters found at \a section_name_c.
+ static std::string SectionNameString(const char* section_name_c);
+
+ //! \brief Returns a segment and section name string.
+ //!
+ //! A segment and section name string is composed of a segment name string
+ //! (see SegmentNameString()) and a section name string (see
+ //! SectionNameString()) separated by a comma. An example is
+ //! `"__TEXT,__text"`.
+ static std::string SegmentAndSectionNameString(const char* segment_name_c,
+ const char* section_name_c);
+
+ private:
+ //! \brief The internal implementation of Name().
+ //!
+ //! This is identical to Name() but does not perform the
+ //! InitializationStateDcheck check. It may be called during initialization
+ //! provided that the caller only does so after segment_command_ has been
+ //! read successfully.
+ std::string NameInternal() const;
+
+ // The segment command data read from the remote process.
+ process_types::segment_command segment_command_;
+
+ // Section structures read from the remote process in the order that they are
+ // given in the remote process.
+ std::vector<process_types::section> sections_;
+
+ // Maps section names to indices into the sections_ vector.
+ std::map<std::string, size_t> section_map_;
+
+ // The image’s slide. Note that the segment’s slide may be 0 and not the value
+ // of the image’s slide if SegmentSlides() is false. In that case, the
+ // segment is extended instead of slid, so its size as loaded will be
+ // increased by this value.
+ mach_vm_size_t slide_;
+
+ InitializationStateDcheck initialized_;
+ InitializationStateDcheck initialized_slide_;
+
+ DISALLOW_COPY_AND_ASSIGN(MachOImageSegmentReader);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SEGMENT_READER_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader_test.cc
new file mode 100644
index 00000000000..bf375af2fad
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader_test.cc
@@ -0,0 +1,188 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/mac/mach_o_image_segment_reader.h"
+
+#include <mach-o/loader.h>
+
+#include "base/basictypes.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+// Most of MachOImageSegmentReader is tested as part of MachOImageReader, which
+// depends on MachOImageSegmentReader to provide major portions of its
+// functionality. Because MachOImageSegmentReader is difficult to use except by
+// a Mach-O load command reader such as MachOImageReader, these portions
+// of MachOImageSegmentReader are not tested independently.
+//
+// The tests here exercise the portions of MachOImageSegmentReader that are
+// exposed and independently useful.
+
+TEST(MachOImageSegmentReader, SegmentNameString) {
+ // The output value should be a string of up to 16 characters, even if the
+ // input value is not NUL-terminated within 16 characters.
+ EXPECT_EQ("__TEXT", MachOImageSegmentReader::SegmentNameString("__TEXT"));
+ EXPECT_EQ("__OVER", MachOImageSegmentReader::SegmentNameString("__OVER"));
+ EXPECT_EQ("", MachOImageSegmentReader::SegmentNameString(""));
+ EXPECT_EQ("p", MachOImageSegmentReader::SegmentNameString("p"));
+ EXPECT_EQ("NoUnderChar",
+ MachOImageSegmentReader::SegmentNameString("NoUnderChar"));
+ EXPECT_EQ("0123456789abcde",
+ MachOImageSegmentReader::SegmentNameString("0123456789abcde"));
+ EXPECT_EQ("0123456789abcdef",
+ MachOImageSegmentReader::SegmentNameString("0123456789abcdef"));
+ EXPECT_EQ("gfedcba987654321",
+ MachOImageSegmentReader::SegmentNameString("gfedcba9876543210"));
+ EXPECT_EQ("hgfedcba98765432",
+ MachOImageSegmentReader::SegmentNameString("hgfedcba9876543210"));
+
+ // Segment names defined in <mach-o/loader.h>. All of these should come
+ // through SegmentNameString() cleanly and without truncation.
+ const char* kSegmentTestData[] = {
+ SEG_TEXT,
+ SEG_DATA,
+ SEG_OBJC,
+ SEG_ICON,
+ SEG_LINKEDIT,
+ SEG_UNIXSTACK,
+ SEG_IMPORT,
+ };
+
+ for (size_t index = 0; index < arraysize(kSegmentTestData); ++index) {
+ EXPECT_EQ(
+ kSegmentTestData[index],
+ MachOImageSegmentReader::SegmentNameString(kSegmentTestData[index]))
+ << base::StringPrintf("index %zu", index);
+ }
+}
+
+TEST(MachOImageSegmentReader, SectionNameString) {
+ // The output value should be a string of up to 16 characters, even if the
+ // input value is not NUL-terminated within 16 characters.
+ EXPECT_EQ("__text", MachOImageSegmentReader::SectionNameString("__text"));
+ EXPECT_EQ("__over", MachOImageSegmentReader::SectionNameString("__over"));
+ EXPECT_EQ("", MachOImageSegmentReader::SectionNameString(""));
+ EXPECT_EQ("p", MachOImageSegmentReader::SectionNameString("p"));
+ EXPECT_EQ("NoUnderChar",
+ MachOImageSegmentReader::SectionNameString("NoUnderChar"));
+ EXPECT_EQ("0123456789abcde",
+ MachOImageSegmentReader::SectionNameString("0123456789abcde"));
+ EXPECT_EQ("0123456789abcdef",
+ MachOImageSegmentReader::SectionNameString("0123456789abcdef"));
+ EXPECT_EQ("gfedcba987654321",
+ MachOImageSegmentReader::SectionNameString("gfedcba9876543210"));
+ EXPECT_EQ("hgfedcba98765432",
+ MachOImageSegmentReader::SectionNameString("hgfedcba9876543210"));
+
+ // Section names defined in <mach-o/loader.h>. All of these should come
+ // through SectionNameString() cleanly and without truncation.
+ const char* kSectionTestData[] = {
+ SECT_TEXT,
+ SECT_FVMLIB_INIT0,
+ SECT_FVMLIB_INIT1,
+ SECT_DATA,
+ SECT_BSS,
+ SECT_COMMON,
+ SECT_OBJC_SYMBOLS,
+ SECT_OBJC_MODULES,
+ SECT_OBJC_STRINGS,
+ SECT_OBJC_REFS,
+ SECT_ICON_HEADER,
+ SECT_ICON_TIFF,
+ };
+
+ for (size_t index = 0; index < arraysize(kSectionTestData); ++index) {
+ EXPECT_EQ(
+ kSectionTestData[index],
+ MachOImageSegmentReader::SectionNameString(kSectionTestData[index]))
+ << base::StringPrintf("index %zu", index);
+ }
+}
+
+TEST(MachOImageSegmentReader, SegmentAndSectionNameString) {
+ struct SegmentAndSectionTestData {
+ const char* segment;
+ const char* section;
+ const char* output;
+ };
+ const SegmentAndSectionTestData kSegmentAndSectionTestData[] = {
+ {"segment", "section", "segment,section"},
+ {"Segment", "Section", "Segment,Section"},
+ {"SEGMENT", "SECTION", "SEGMENT,SECTION"},
+ {"__TEXT", "__plain", "__TEXT,__plain"},
+ {"__TEXT", "poetry", "__TEXT,poetry"},
+ {"__TEXT", "Prose", "__TEXT,Prose"},
+ {"__PLAIN", "__text", "__PLAIN,__text"},
+ {"rich", "__text", "rich,__text"},
+ {"segment", "", "segment,"},
+ {"", "section", ",section"},
+ {"", "", ","},
+ {"0123456789abcdef", "section", "0123456789abcdef,section"},
+ {"gfedcba9876543210", "section", "gfedcba987654321,section"},
+ {"0123456789abcdef", "", "0123456789abcdef,"},
+ {"gfedcba9876543210", "", "gfedcba987654321,"},
+ {"segment", "0123456789abcdef", "segment,0123456789abcdef"},
+ {"segment", "gfedcba9876543210", "segment,gfedcba987654321"},
+ {"", "0123456789abcdef", ",0123456789abcdef"},
+ {"", "gfedcba9876543210", ",gfedcba987654321"},
+ {"0123456789abcdef",
+ "0123456789abcdef",
+ "0123456789abcdef,0123456789abcdef"},
+ {"gfedcba9876543210",
+ "gfedcba9876543210",
+ "gfedcba987654321,gfedcba987654321"},
+
+ // Sections defined in <mach-o/loader.h>. All of these should come through
+ // SegmentAndSectionNameString() cleanly and without truncation.
+ {SEG_TEXT, SECT_TEXT, "__TEXT,__text"},
+ {SEG_TEXT, SECT_FVMLIB_INIT0, "__TEXT,__fvmlib_init0"},
+ {SEG_TEXT, SECT_FVMLIB_INIT1, "__TEXT,__fvmlib_init1"},
+ {SEG_DATA, SECT_DATA, "__DATA,__data"},
+ {SEG_DATA, SECT_BSS, "__DATA,__bss"},
+ {SEG_DATA, SECT_COMMON, "__DATA,__common"},
+ {SEG_OBJC, SECT_OBJC_SYMBOLS, "__OBJC,__symbol_table"},
+ {SEG_OBJC, SECT_OBJC_MODULES, "__OBJC,__module_info"},
+ {SEG_OBJC, SECT_OBJC_STRINGS, "__OBJC,__selector_strs"},
+ {SEG_OBJC, SECT_OBJC_REFS, "__OBJC,__selector_refs"},
+ {SEG_ICON, SECT_ICON_HEADER, "__ICON,__header"},
+ {SEG_ICON, SECT_ICON_TIFF, "__ICON,__tiff"},
+
+ // These segments don’t normally have sections, but the above group tested
+ // the known segment names for segments that do normally have sections.
+ // This group does the same for segments that normally don’t.
+ {SEG_LINKEDIT, "", "__LINKEDIT,"},
+ {SEG_UNIXSTACK, "", "__UNIXSTACK,"},
+ {SEG_IMPORT, "", "__IMPORT,"},
+ };
+
+ for (size_t index = 0; index < arraysize(kSegmentAndSectionTestData);
+ ++index) {
+ const SegmentAndSectionTestData& test = kSegmentAndSectionTestData[index];
+ EXPECT_EQ(test.output,
+ MachOImageSegmentReader::SegmentAndSectionNameString(
+ test.segment, test.section))
+ << base::StringPrintf("index %zu, segment %s, section %s",
+ index,
+ test.segment,
+ test.section);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_symbol_table_reader.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_symbol_table_reader.cc
new file mode 100644
index 00000000000..949e4608869
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_symbol_table_reader.cc
@@ -0,0 +1,292 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/mac/mach_o_image_symbol_table_reader.h"
+
+#include <mach-o/loader.h>
+#include <mach-o/nlist.h>
+
+#include <utility>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/stringprintf.h"
+#include "util/mac/checked_mach_address_range.h"
+#include "util/mach/task_memory.h"
+
+namespace crashpad {
+
+namespace internal {
+
+//! \brief The internal implementation for MachOImageSymbolTableReader.
+//!
+//! Initialization is broken into more than one function that needs to share
+//! data, so member variables are used. However, much of this data is irrelevant
+//! after initialization is completed, so rather than doing it in
+//! MachOImageSymbolTableReader, it’s handled by this class, which is a “friend”
+//! of MachOImageSymbolTableReader.
+class MachOImageSymbolTableReaderInitializer {
+ public:
+ MachOImageSymbolTableReaderInitializer(
+ ProcessReader* process_reader,
+ const MachOImageSegmentReader* linkedit_segment,
+ const std::string& module_info)
+ : module_info_(module_info),
+ linkedit_range_(),
+ process_reader_(process_reader),
+ linkedit_segment_(linkedit_segment) {
+ linkedit_range_.SetRange(process_reader_->Is64Bit(),
+ linkedit_segment->Address(),
+ linkedit_segment->Size());
+ DCHECK(linkedit_range_.IsValid());
+ }
+
+ ~MachOImageSymbolTableReaderInitializer() {}
+
+ //! \brief Reads the symbol table from another process.
+ //!
+ //! \sa MachOImageSymbolTableReader::Initialize()
+ bool Initialize(const process_types::symtab_command* symtab_command,
+ const process_types::dysymtab_command* dysymtab_command,
+ MachOImageSymbolTableReader::SymbolInformationMap*
+ external_defined_symbols) {
+ mach_vm_address_t symtab_address =
+ AddressForLinkEditComponent(symtab_command->symoff);
+ uint32_t symbol_count = symtab_command->nsyms;
+ size_t nlist_size = process_types::nlist::ExpectedSize(process_reader_);
+ mach_vm_size_t symtab_size = symbol_count * nlist_size;
+ if (!IsInLinkEditSegment(symtab_address, symtab_size, "symtab")) {
+ return false;
+ }
+
+ // If a dysymtab is present, use it to filter the symtab for just the
+ // portion used for extdefsym. If no dysymtab is present, the entire symtab
+ // will need to be consulted.
+ uint32_t skip_count = 0;
+ if (dysymtab_command) {
+ if (dysymtab_command->iextdefsym >= symtab_command->nsyms ||
+ dysymtab_command->iextdefsym + dysymtab_command->nextdefsym >
+ symtab_command->nsyms) {
+ LOG(WARNING) << base::StringPrintf(
+ "dysymtab extdefsym %u + %u > symtab nsyms %u",
+ dysymtab_command->iextdefsym,
+ dysymtab_command->nextdefsym,
+ symtab_command->nsyms) << module_info_;
+ return false;
+ }
+
+ skip_count = dysymtab_command->iextdefsym;
+ mach_vm_size_t skip_size = skip_count * nlist_size;
+ symtab_address += skip_size;
+ symtab_size -= skip_size;
+ symbol_count = dysymtab_command->nextdefsym;
+ }
+
+ mach_vm_address_t strtab_address =
+ AddressForLinkEditComponent(symtab_command->stroff);
+ mach_vm_size_t strtab_size = symtab_command->strsize;
+ if (!IsInLinkEditSegment(strtab_address, strtab_size, "strtab")) {
+ return false;
+ }
+
+ scoped_ptr<process_types::nlist[]> symbols(
+ new process_types::nlist[symtab_command->nsyms]);
+ if (!process_types::nlist::ReadArrayInto(
+ process_reader_, symtab_address, symbol_count, &symbols[0])) {
+ LOG(WARNING) << "could not read symbol table" << module_info_;
+ return false;
+ }
+
+ scoped_ptr<TaskMemory::MappedMemory> string_table;
+ for (size_t symbol_index = 0; symbol_index < symbol_count; ++symbol_index) {
+ const process_types::nlist& symbol = symbols[symbol_index];
+ std::string symbol_info = base::StringPrintf(", symbol index %zu%s",
+ skip_count + symbol_index,
+ module_info_.c_str());
+ bool valid_symbol = true;
+ if ((symbol.n_type & N_STAB) == 0 && (symbol.n_type & N_PEXT) == 0 &&
+ (symbol.n_type & N_EXT)) {
+ uint8_t symbol_type = symbol.n_type & N_TYPE;
+ if (symbol_type == N_ABS || symbol_type == N_SECT) {
+ if (symbol.n_strx >= strtab_size) {
+ LOG(WARNING) << base::StringPrintf(
+ "string at 0x%x out of bounds (0x%llx)",
+ symbol.n_strx,
+ strtab_size) << symbol_info;
+ return false;
+ }
+
+ if (!string_table) {
+ string_table = process_reader_->Memory()->ReadMapped(
+ strtab_address, strtab_size);
+ if (!string_table) {
+ LOG(WARNING) << "could not read string table" << module_info_;
+ return false;
+ }
+ }
+
+ std::string name;
+ if (!string_table->ReadCString(symbol.n_strx, &name)) {
+ LOG(WARNING) << "could not read string" << symbol_info;
+ return false;
+ }
+
+ if (symbol_type == N_ABS && symbol.n_sect != NO_SECT) {
+ LOG(WARNING) << base::StringPrintf("N_ABS symbol %s in section %u",
+ name.c_str(),
+ symbol.n_sect) << symbol_info;
+ return false;
+ }
+
+ if (symbol_type == N_SECT && symbol.n_sect == NO_SECT) {
+ LOG(WARNING) << base::StringPrintf(
+ "N_SECT symbol %s in section NO_SECT",
+ name.c_str()) << symbol_info;
+ return false;
+ }
+
+ MachOImageSymbolTableReader::SymbolInformation this_symbol_info;
+ this_symbol_info.value = symbol.n_value;
+ this_symbol_info.section = symbol.n_sect;
+ if (!external_defined_symbols->insert(
+ std::make_pair(name, this_symbol_info)).second) {
+ LOG(WARNING) << "duplicate symbol " << name << symbol_info;
+ return false;
+ }
+ } else {
+ // External indirect symbols may be found in the portion of the symbol
+ // table used for external symbols as opposed to indirect symbols when
+ // the indirect symbols are also external. These can be produced by
+ // Xcode 5.1 ld64-236.3/src/ld/LinkEditClassic.hpp
+ // ld::tool::SymbolTableAtom<>::addGlobal(). Indirect symbols are not
+ // currently supported by this symbol table reader, so ignore them
+ // without failing or logging a message when encountering them. See
+ // https://groups.google.com/a/chromium.org/d/topic/crashpad-dev/k7QkLwO71Zo
+ valid_symbol = symbol_type == N_INDR;
+ }
+ } else {
+ valid_symbol = false;
+ }
+ if (!valid_symbol && dysymtab_command) {
+ LOG(WARNING) << "non-external symbol with type " << symbol.n_type
+ << " in extdefsym" << symbol_info;
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private:
+ //! \brief Computes the address for data in the `__LINKEDIT` segment
+ //! identified by its file offset in a Mach-O image.
+ //!
+ //! \param[in] fileoff The file offset relative to the beginning of an image’s
+ //! `mach_header` or `mach_header_64` of the data in the `__LINKEDIT`
+ //! segment.
+ //!
+ //! \return The address, in the remote process’ address space, of the
+ //! requested data.
+ mach_vm_address_t AddressForLinkEditComponent(uint32_t fileoff) const {
+ return linkedit_range_.Base() + fileoff - linkedit_segment_->fileoff();
+ }
+
+ //! \brief Determines whether an address range is located within the
+ //! `__LINKEDIT` segment.
+ //!
+ //! \param[in] address The base address of the range to check.
+ //! \param[in] size The size of the range to check.
+ //! \param[in] tag A string that identifies the range being checked. This is
+ //! used only for logging.
+ //!
+ //! \return `true` if the range identified by \a address + \a size lies
+ //! entirely within the `__LINKEDIT` segment. `false` if that range is
+ //! invalid, or if that range is not contained by the `__LINKEDIT`
+ //! segment, with an appropriate message logged.
+ bool IsInLinkEditSegment(mach_vm_address_t address,
+ mach_vm_size_t size,
+ const char* tag) const {
+ CheckedMachAddressRange subrange(process_reader_->Is64Bit(), address, size);
+ if (!subrange.IsValid()) {
+ LOG(WARNING) << base::StringPrintf("invalid %s range (0x%llx + 0x%llx)",
+ tag,
+ address,
+ size) << module_info_;
+ return false;
+ }
+
+ if (!linkedit_range_.ContainsRange(subrange)) {
+ LOG(WARNING) << base::StringPrintf(
+ "%s at 0x%llx + 0x%llx outside of " SEG_LINKEDIT
+ " segment at 0x%llx + 0x%llx",
+ tag,
+ address,
+ size,
+ linkedit_range_.Base(),
+ linkedit_range_.Size()) << module_info_;
+ return false;
+ }
+
+ return true;
+ }
+
+ std::string module_info_;
+ CheckedMachAddressRange linkedit_range_;
+ ProcessReader* process_reader_; // weak
+ const MachOImageSegmentReader* linkedit_segment_; // weak
+
+ DISALLOW_COPY_AND_ASSIGN(MachOImageSymbolTableReaderInitializer);
+};
+
+} // namespace internal
+
+MachOImageSymbolTableReader::MachOImageSymbolTableReader()
+ : external_defined_symbols_(), initialized_() {
+}
+
+MachOImageSymbolTableReader::~MachOImageSymbolTableReader() {
+}
+
+bool MachOImageSymbolTableReader::Initialize(
+ ProcessReader* process_reader,
+ const process_types::symtab_command* symtab_command,
+ const process_types::dysymtab_command* dysymtab_command,
+ const MachOImageSegmentReader* linkedit_segment,
+ const std::string& module_info) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ internal::MachOImageSymbolTableReaderInitializer initializer(process_reader,
+ linkedit_segment,
+ module_info);
+ if (!initializer.Initialize(
+ symtab_command, dysymtab_command, &external_defined_symbols_)) {
+ return false;
+ }
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+const MachOImageSymbolTableReader::SymbolInformation*
+MachOImageSymbolTableReader::LookUpExternalDefinedSymbol(
+ const std::string& name) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ const auto& iterator = external_defined_symbols_.find(name);
+ if (iterator == external_defined_symbols_.end()) {
+ return nullptr;
+ }
+ return &iterator->second;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_symbol_table_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_symbol_table_reader.h
new file mode 100644
index 00000000000..9302e7fc000
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_symbol_table_reader.h
@@ -0,0 +1,135 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SYMBOL_TABLE_READER_H_
+#define CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SYMBOL_TABLE_READER_H_
+
+#include "base/basictypes.h"
+
+#include <map>
+#include <string>
+
+#include <mach/mach.h>
+#include <stdint.h>
+
+#include "snapshot/mac/mach_o_image_segment_reader.h"
+#include "snapshot/mac/process_reader.h"
+#include "snapshot/mac/process_types.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+
+//! \brief A reader for symbol tables in Mach-O images mapped into another
+//! process.
+class MachOImageSymbolTableReader {
+ public:
+ //! \brief Information about a symbol in a module’s symbol table.
+ //!
+ //! This is a more minimal form of the `nlist` (or `nlist_64`) structure,
+ //! only containing the equivalent of the `n_value` and `n_sect` fields.
+ struct SymbolInformation {
+ //! \brief The address of the symbol as it exists in the symbol table, not
+ //! adjusted for any “slide.”
+ mach_vm_address_t value;
+
+ //! \brief The 1-based section index in the module in which the symbol is
+ //! found.
+ //!
+ //! For symbols defined in a section (`N_SECT`), this is the section index
+ //! that can be passed to MachOImageReader::GetSectionAtIndex(), and \a
+ //! value will need to be adjusted for segment slide if the containing
+ //! segment slid when loaded. For absolute symbols (`N_ABS`), this will be
+ //! `NO_SECT` (`0`), and \a value must not be adjusted for segment slide.
+ uint8_t section;
+ };
+
+ // TODO(mark): Use unordered_map or a similar hash-based map? For now,
+ // std::map is fine because this map only stores external defined symbols,
+ // and there aren’t expected to be very many of those that performance would
+ // become a problem. std::map is also guaranteed to be part of the standard
+ // library, which isn’t the case for std::unordered_map, which requires the
+ // C++11 library. In reality, std::unordered_map does not appear to provide
+ // a performance advantage. It appears that the memory copies currently done
+ // by TaskMemory::Read() have substantially more impact on symbol table
+ // operations.
+ //
+ // This is public so that the type is available to
+ // MachOImageSymbolTableReaderInitializer.
+ using SymbolInformationMap = std::map<std::string, SymbolInformation>;
+
+ MachOImageSymbolTableReader();
+ ~MachOImageSymbolTableReader();
+
+ //! \brief Reads the symbol table from another process.
+ //!
+ //! This method must only be called once on an object. This method must be
+ //! called successfully before any other method in this class may be called.
+ //!
+ //! \param[in] process_reader The reader for the remote process.
+ //! \param[in] symtab_command The `LC_SYMTAB` load command that identifies
+ //! the symbol table.
+ //! \param[in] dysymtab_command The `LC_DYSYMTAB` load command that identifies
+ //! dynamic symbol information within the symbol table. This load command
+ //! is not present in all modules, and this parameter may be `nullptr` for
+ //! modules that do not have this information. When present, \a
+ //! dysymtab_command is an optimization that allows the symbol table
+ //! reader to only examine symbol table entries known to be relevant for
+ //! its purposes.
+ //! \param[in] linkedit_segment The `__LINKEDIT` segment. This segment should
+ //! contain the data referenced by \a symtab_command and \a
+ //! dysymtab_command. This may be any segment in the module, but by
+ //! convention, the name `__LINKEDIT` is used for this purpose.
+ //! \param[in] module_info A string to be used in logged messages. This string
+ //! is for diagnostic purposes only, and may be empty.
+ //!
+ //! \return `true` if the symbol table was read successfully. `false`
+ //! otherwise, with an appropriate message logged.
+ bool Initialize(ProcessReader* process_reader,
+ const process_types::symtab_command* symtab_command,
+ const process_types::dysymtab_command* dysymtab_command,
+ const MachOImageSegmentReader* linkedit_segment,
+ const std::string& module_info);
+
+ //! \brief Looks up a symbol in the image’s symbol table.
+ //!
+ //! The returned information captures the symbol as it exists in the image’s
+ //! symbol table, not adjusted for any “slide.”
+ //!
+ //! \param[in] name The name of the symbol to look up, “mangled” or
+ //! “decorated” appropriately. For example, use `"_main"` to look up the
+ //! symbol for the C `main()` function, and use `"__Z4Funcv"` to look up
+ //! the symbol for the C++ `Func()` function.
+ //!
+ //! \return A SymbolInformation* object with information about the symbol if
+ //! it was found, or `nullptr` if the symbol was not found or if an error
+ //! occurred. On error, a warning message will also be logged. The caller
+ //! does not take ownership; the lifetime of the returned object is scoped
+ //! to the lifetime of this MachOImageSymbolTableReader object.
+ //!
+ //! \note Symbol values returned via this interface are not adjusted for
+ //! “slide.” For slide-adjusted values, use the higher-level
+ //! MachOImageReader::LookUpExternalDefinedSymbol() interface.
+ const SymbolInformation* LookUpExternalDefinedSymbol(
+ const std::string& name) const;
+
+ private:
+ SymbolInformationMap external_defined_symbols_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(MachOImageSymbolTableReader);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SYMBOL_TABLE_READER_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.cc
new file mode 100644
index 00000000000..b0cce244a38
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.cc
@@ -0,0 +1,68 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/mac/memory_snapshot_mac.h"
+
+#include "util/mach/task_memory.h"
+
+namespace crashpad {
+namespace internal {
+
+MemorySnapshotMac::MemorySnapshotMac()
+ : MemorySnapshot(),
+ process_reader_(nullptr),
+ address_(0),
+ size_(0),
+ initialized_() {
+}
+
+MemorySnapshotMac::~MemorySnapshotMac() {
+}
+
+void MemorySnapshotMac::Initialize(ProcessReader* process_reader,
+ uint64_t address,
+ uint64_t size) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+ process_reader_ = process_reader;
+ address_ = address;
+ size_ = size;
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+}
+
+uint64_t MemorySnapshotMac::Address() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return address_;
+}
+
+size_t MemorySnapshotMac::Size() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return size_;
+}
+
+bool MemorySnapshotMac::Read(Delegate* delegate) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ if (size_ == 0) {
+ return delegate->MemorySnapshotDelegateRead(nullptr, size_);
+ }
+
+ scoped_ptr<uint8_t[]> buffer(new uint8_t[size_]);
+ if (!process_reader_->Memory()->Read(address_, size_, buffer.get())) {
+ return false;
+ }
+ return delegate->MemorySnapshotDelegateRead(buffer.get(), size_);
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.h b/chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.h
new file mode 100644
index 00000000000..2f190824003
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.h
@@ -0,0 +1,68 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MAC_MEMORY_SNAPSHOT_MAC_H_
+#define CRASHPAD_SNAPSHOT_MAC_MEMORY_SNAPSHOT_MAC_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "base/basictypes.h"
+#include "snapshot/mac/process_reader.h"
+#include "snapshot/memory_snapshot.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+namespace internal {
+
+//! \brief A MemorySnapshot of a memory region in a process on the running
+//! system, when the system runs Mac OS X.
+class MemorySnapshotMac final : public MemorySnapshot {
+ public:
+ MemorySnapshotMac();
+ ~MemorySnapshotMac() override;
+
+ //! \brief Initializes the object.
+ //!
+ //! Memory is read lazily. No attempt is made to read the memory snapshot data
+ //! until Read() is called, and the memory snapshot data is discared when
+ //! Read() returns.
+ //!
+ //! \param[in] process_reader A reader for the process being snapshotted.
+ //! \param[in] address The base address of the memory region to snapshot, in
+ //! the snapshot process’ address space.
+ //! \param[in] size The size of the memory region to snapshot.
+ void Initialize(ProcessReader* process_reader,
+ uint64_t address,
+ uint64_t size);
+
+ // MemorySnapshot:
+
+ uint64_t Address() const override;
+ size_t Size() const override;
+ bool Read(Delegate* delegate) const override;
+
+ private:
+ ProcessReader* process_reader_; // weak
+ uint64_t address_;
+ uint64_t size_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(MemorySnapshotMac);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MAC_MEMORY_SNAPSHOT_MAC_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/module_snapshot_mac.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/module_snapshot_mac.cc
new file mode 100644
index 00000000000..cc7e1863edd
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/module_snapshot_mac.cc
@@ -0,0 +1,174 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/mac/module_snapshot_mac.h"
+
+#include <mach/mach.h>
+#include <mach-o/loader.h>
+
+#include "base/strings/stringprintf.h"
+#include "snapshot/mac/mach_o_image_annotations_reader.h"
+#include "snapshot/mac/mach_o_image_reader.h"
+#include "util/misc/tri_state.h"
+#include "util/misc/uuid.h"
+#include "util/stdlib/strnlen.h"
+
+namespace crashpad {
+namespace internal {
+
+ModuleSnapshotMac::ModuleSnapshotMac()
+ : ModuleSnapshot(),
+ name_(),
+ timestamp_(0),
+ mach_o_image_reader_(nullptr),
+ process_reader_(nullptr),
+ initialized_() {
+}
+
+ModuleSnapshotMac::~ModuleSnapshotMac() {
+}
+
+bool ModuleSnapshotMac::Initialize(
+ ProcessReader* process_reader,
+ const ProcessReader::Module& process_reader_module) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ process_reader_ = process_reader;
+ name_ = process_reader_module.name;
+ timestamp_ = process_reader_module.timestamp;
+ mach_o_image_reader_ = process_reader_module.reader;
+ if (!mach_o_image_reader_) {
+ return false;
+ }
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+void ModuleSnapshotMac::GetCrashpadOptions(CrashpadInfoClientOptions* options) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ process_types::CrashpadInfo crashpad_info;
+ if (!mach_o_image_reader_->GetCrashpadInfo(&crashpad_info)) {
+ options->crashpad_handler_behavior = TriState::kUnset;
+ options->system_crash_reporter_forwarding = TriState::kUnset;
+ return;
+ }
+
+ options->crashpad_handler_behavior =
+ CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
+ crashpad_info.crashpad_handler_behavior);
+
+ options->system_crash_reporter_forwarding =
+ CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
+ crashpad_info.system_crash_reporter_forwarding);
+}
+
+std::string ModuleSnapshotMac::Name() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return name_;
+}
+
+uint64_t ModuleSnapshotMac::Address() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return mach_o_image_reader_->Address();
+}
+
+uint64_t ModuleSnapshotMac::Size() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return mach_o_image_reader_->Size();
+}
+
+time_t ModuleSnapshotMac::Timestamp() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return timestamp_;
+}
+
+void ModuleSnapshotMac::FileVersion(uint16_t* version_0,
+ uint16_t* version_1,
+ uint16_t* version_2,
+ uint16_t* version_3) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ if (mach_o_image_reader_->FileType() == MH_DYLIB) {
+ uint32_t dylib_version = mach_o_image_reader_->DylibVersion();
+ *version_0 = (dylib_version & 0xffff0000) >> 16;
+ *version_1 = (dylib_version & 0x0000ff00) >> 8;
+ *version_2 = (dylib_version & 0x000000ff);
+ *version_3 = 0;
+ } else {
+ *version_0 = 0;
+ *version_1 = 0;
+ *version_2 = 0;
+ *version_3 = 0;
+ }
+}
+
+void ModuleSnapshotMac::SourceVersion(uint16_t* version_0,
+ uint16_t* version_1,
+ uint16_t* version_2,
+ uint16_t* version_3) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ // LC_SOURCE_VERSION is supposed to be interpreted as a 5-component version
+ // number, 24 bits for the first component and 10 for the others, per
+ // <mach-o/loader.h>. To preserve the full range of possible version numbers
+ // without data loss, map it to the 4 16-bit fields mandated by the interface
+ // here, which was informed by the minidump file format.
+ uint64_t source_version = mach_o_image_reader_->SourceVersion();
+ *version_0 = (source_version & 0xffff000000000000u) >> 48;
+ *version_1 = (source_version & 0x0000ffff00000000u) >> 32;
+ *version_2 = (source_version & 0x00000000ffff0000u) >> 16;
+ *version_3 = source_version & 0x000000000000ffffu;
+}
+
+ModuleSnapshot::ModuleType ModuleSnapshotMac::GetModuleType() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ uint32_t file_type = mach_o_image_reader_->FileType();
+ switch (file_type) {
+ case MH_EXECUTE:
+ return kModuleTypeExecutable;
+ case MH_DYLIB:
+ return kModuleTypeSharedLibrary;
+ case MH_DYLINKER:
+ return kModuleTypeDynamicLoader;
+ case MH_BUNDLE:
+ return kModuleTypeLoadableModule;
+ default:
+ return kModuleTypeUnknown;
+ }
+}
+
+void ModuleSnapshotMac::UUID(crashpad::UUID* uuid) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return mach_o_image_reader_->UUID(uuid);
+}
+
+std::vector<std::string> ModuleSnapshotMac::AnnotationsVector() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ MachOImageAnnotationsReader annotations_reader(
+ process_reader_, mach_o_image_reader_, name_);
+ return annotations_reader.Vector();
+}
+
+std::map<std::string, std::string> ModuleSnapshotMac::AnnotationsSimpleMap()
+ const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ MachOImageAnnotationsReader annotations_reader(
+ process_reader_, mach_o_image_reader_, name_);
+ return annotations_reader.SimpleMap();
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/module_snapshot_mac.h b/chromium/third_party/crashpad/crashpad/snapshot/mac/module_snapshot_mac.h
new file mode 100644
index 00000000000..fc60e4004e7
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/module_snapshot_mac.h
@@ -0,0 +1,95 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MAC_MODULE_SNAPSHOT_MAC_H_
+#define CRASHPAD_SNAPSHOT_MAC_MODULE_SNAPSHOT_MAC_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "client/crashpad_info.h"
+#include "snapshot/crashpad_info_client_options.h"
+#include "snapshot/mac/process_reader.h"
+#include "snapshot/module_snapshot.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+
+class MachOImageReader;
+struct UUID;
+
+namespace internal {
+
+//! \brief A ModuleSnapshot of a code module (binary image) loaded into a
+//! running (or crashed) process on a Mac OS X system.
+class ModuleSnapshotMac final : public ModuleSnapshot {
+ public:
+ ModuleSnapshotMac();
+ ~ModuleSnapshotMac() override;
+
+ //! \brief Initializes the object.
+ //!
+ //! \param[in] process_reader A ProcessReader for the task containing the
+ //! module.
+ //! \param[in] process_reader_module The module within the ProcessReader for
+ //! which the snapshot should be created.
+ //!
+ //! \return `true` if the snapshot could be created, `false` otherwise with
+ //! an appropriate message logged.
+ bool Initialize(ProcessReader* process_reader,
+ const ProcessReader::Module& process_reader_module);
+
+ //! \brief Returns options from the module’s CrashpadInfo structure.
+ //!
+ //! \param[out] options Options set in the module’s CrashpadInfo structure.
+ void GetCrashpadOptions(CrashpadInfoClientOptions* options);
+
+ // ModuleSnapshot:
+
+ std::string Name() const override;
+ uint64_t Address() const override;
+ uint64_t Size() const override;
+ time_t Timestamp() const override;
+ void FileVersion(uint16_t* version_0,
+ uint16_t* version_1,
+ uint16_t* version_2,
+ uint16_t* version_3) const override;
+ void SourceVersion(uint16_t* version_0,
+ uint16_t* version_1,
+ uint16_t* version_2,
+ uint16_t* version_3) const override;
+ ModuleType GetModuleType() const override;
+ void UUID(crashpad::UUID* uuid) const override;
+ std::vector<std::string> AnnotationsVector() const override;
+ std::map<std::string, std::string> AnnotationsSimpleMap() const override;
+
+ private:
+ std::string name_;
+ time_t timestamp_;
+ const MachOImageReader* mach_o_image_reader_; // weak
+ ProcessReader* process_reader_; // weak
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotMac);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MAC_MODULE_SNAPSHOT_MAC_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/process_reader.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_reader.cc
new file mode 100644
index 00000000000..766367584e5
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_reader.cc
@@ -0,0 +1,700 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/mac/process_reader.h"
+
+#include <AvailabilityMacros.h>
+#include <mach/mach_vm.h>
+#include <mach-o/loader.h>
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/mac/scoped_mach_vm.h"
+#include "base/strings/stringprintf.h"
+#include "snapshot/mac/mach_o_image_reader.h"
+#include "snapshot/mac/process_types.h"
+#include "util/misc/scoped_forbid_return.h"
+
+namespace {
+
+void MachTimeValueToTimeval(const time_value& mach, timeval* tv) {
+ tv->tv_sec = mach.seconds;
+ tv->tv_usec = mach.microseconds;
+}
+
+kern_return_t MachVMRegionRecurseDeepest(task_t task,
+ mach_vm_address_t* address,
+ mach_vm_size_t* size,
+ natural_t* depth,
+ vm_prot_t* protection,
+ unsigned int* user_tag) {
+ vm_region_submap_short_info_64 submap_info;
+ mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
+ while (true) {
+ kern_return_t kr = mach_vm_region_recurse(
+ task,
+ address,
+ size,
+ depth,
+ reinterpret_cast<vm_region_recurse_info_t>(&submap_info),
+ &count);
+ if (kr != KERN_SUCCESS) {
+ return kr;
+ }
+
+ if (!submap_info.is_submap) {
+ *protection = submap_info.protection;
+ *user_tag = submap_info.user_tag;
+ return KERN_SUCCESS;
+ }
+
+ ++*depth;
+ }
+}
+
+} // namespace
+
+namespace crashpad {
+
+ProcessReader::Thread::Thread()
+ : thread_context(),
+ float_context(),
+ debug_context(),
+ id(0),
+ stack_region_address(0),
+ stack_region_size(0),
+ thread_specific_data_address(0),
+ port(THREAD_NULL),
+ suspend_count(0),
+ priority(0) {
+}
+
+ProcessReader::Module::Module() : name(), reader(nullptr), timestamp(0) {
+}
+
+ProcessReader::Module::~Module() {
+}
+
+ProcessReader::ProcessReader()
+ : process_info_(),
+ threads_(),
+ modules_(),
+ module_readers_(),
+ task_memory_(),
+ task_(TASK_NULL),
+ initialized_(),
+ is_64_bit_(false),
+ initialized_threads_(false),
+ initialized_modules_(false) {
+}
+
+ProcessReader::~ProcessReader() {
+ for (const Thread& thread : threads_) {
+ kern_return_t kr = mach_port_deallocate(mach_task_self(), thread.port);
+ MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_port_deallocate";
+ }
+}
+
+bool ProcessReader::Initialize(task_t task) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ if (!process_info_.InitializeFromTask(task)) {
+ return false;
+ }
+
+ is_64_bit_ = process_info_.Is64Bit();
+
+ task_memory_.reset(new TaskMemory(task));
+ task_ = task;
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+bool ProcessReader::CPUTimes(timeval* user_time, timeval* system_time) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ // Calculate user and system time the same way the kernel does for
+ // getrusage(). See 10.9.2 xnu-2422.90.20/bsd/kern/kern_resource.c calcru().
+ timerclear(user_time);
+ timerclear(system_time);
+
+ // As of the 10.8 SDK, the preferred routine is MACH_TASK_BASIC_INFO.
+ // TASK_BASIC_INFO_64 is equivalent and works on earlier systems.
+ task_basic_info_64 task_basic_info;
+ mach_msg_type_number_t task_basic_info_count = TASK_BASIC_INFO_64_COUNT;
+ kern_return_t kr = task_info(task_,
+ TASK_BASIC_INFO_64,
+ reinterpret_cast<task_info_t>(&task_basic_info),
+ &task_basic_info_count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(WARNING, kr) << "task_info TASK_BASIC_INFO_64";
+ return false;
+ }
+
+ task_thread_times_info_data_t task_thread_times;
+ mach_msg_type_number_t task_thread_times_count = TASK_THREAD_TIMES_INFO_COUNT;
+ kr = task_info(task_,
+ TASK_THREAD_TIMES_INFO,
+ reinterpret_cast<task_info_t>(&task_thread_times),
+ &task_thread_times_count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(WARNING, kr) << "task_info TASK_THREAD_TIMES";
+ return false;
+ }
+
+ MachTimeValueToTimeval(task_basic_info.user_time, user_time);
+ MachTimeValueToTimeval(task_basic_info.system_time, system_time);
+
+ timeval thread_user_time;
+ MachTimeValueToTimeval(task_thread_times.user_time, &thread_user_time);
+ timeval thread_system_time;
+ MachTimeValueToTimeval(task_thread_times.system_time, &thread_system_time);
+
+ timeradd(user_time, &thread_user_time, user_time);
+ timeradd(system_time, &thread_system_time, system_time);
+
+ return true;
+}
+
+const std::vector<ProcessReader::Thread>& ProcessReader::Threads() {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ if (!initialized_threads_) {
+ InitializeThreads();
+ }
+
+ return threads_;
+}
+
+const std::vector<ProcessReader::Module>& ProcessReader::Modules() {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ if (!initialized_modules_) {
+ InitializeModules();
+ }
+
+ return modules_;
+}
+
+void ProcessReader::InitializeThreads() {
+ DCHECK(!initialized_threads_);
+ DCHECK(threads_.empty());
+
+ initialized_threads_ = true;
+
+ thread_act_array_t threads;
+ mach_msg_type_number_t thread_count = 0;
+ kern_return_t kr = task_threads(task_, &threads, &thread_count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(WARNING, kr) << "task_threads";
+ return;
+ }
+
+ // The send rights in the |threads| array won’t have their send rights managed
+ // by anything until they’re added to |threads_| by the loop below. Any early
+ // return (or exception) that happens between here and the completion of the
+ // loop below will leak thread port send rights.
+ ScopedForbidReturn threads_need_owners;
+
+ base::mac::ScopedMachVM threads_vm(
+ reinterpret_cast<vm_address_t>(threads),
+ mach_vm_round_page(thread_count * sizeof(*threads)));
+
+ for (size_t index = 0; index < thread_count; ++index) {
+ Thread thread;
+ thread.port = threads[index];
+
+#if defined(ARCH_CPU_X86_FAMILY)
+ const thread_state_flavor_t kThreadStateFlavor =
+ Is64Bit() ? x86_THREAD_STATE64 : x86_THREAD_STATE32;
+ mach_msg_type_number_t thread_state_count =
+ Is64Bit() ? x86_THREAD_STATE64_COUNT : x86_THREAD_STATE32_COUNT;
+
+ // TODO(mark): Use the AVX variants instead of the FLOAT variants?
+ const thread_state_flavor_t kFloatStateFlavor =
+ Is64Bit() ? x86_FLOAT_STATE64 : x86_FLOAT_STATE32;
+ mach_msg_type_number_t float_state_count =
+ Is64Bit() ? x86_FLOAT_STATE64_COUNT : x86_FLOAT_STATE32_COUNT;
+
+ const thread_state_flavor_t kDebugStateFlavor =
+ Is64Bit() ? x86_DEBUG_STATE64 : x86_DEBUG_STATE32;
+ mach_msg_type_number_t debug_state_count =
+ Is64Bit() ? x86_DEBUG_STATE64_COUNT : x86_DEBUG_STATE32_COUNT;
+#endif
+
+ kr = thread_get_state(
+ thread.port,
+ kThreadStateFlavor,
+ reinterpret_cast<thread_state_t>(&thread.thread_context),
+ &thread_state_count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "thread_get_state(" << kThreadStateFlavor << ")";
+ continue;
+ }
+
+ kr = thread_get_state(
+ thread.port,
+ kFloatStateFlavor,
+ reinterpret_cast<thread_state_t>(&thread.float_context),
+ &float_state_count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "thread_get_state(" << kFloatStateFlavor << ")";
+ continue;
+ }
+
+ kr = thread_get_state(
+ thread.port,
+ kDebugStateFlavor,
+ reinterpret_cast<thread_state_t>(&thread.debug_context),
+ &debug_state_count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "thread_get_state(" << kDebugStateFlavor << ")";
+ continue;
+ }
+
+ thread_basic_info basic_info;
+ mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
+ kr = thread_info(thread.port,
+ THREAD_BASIC_INFO,
+ reinterpret_cast<thread_info_t>(&basic_info),
+ &count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(WARNING, kr) << "thread_info(THREAD_BASIC_INFO)";
+ } else {
+ thread.suspend_count = basic_info.suspend_count;
+ }
+
+ thread_identifier_info identifier_info;
+ count = THREAD_IDENTIFIER_INFO_COUNT;
+ kr = thread_info(thread.port,
+ THREAD_IDENTIFIER_INFO,
+ reinterpret_cast<thread_info_t>(&identifier_info),
+ &count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(WARNING, kr) << "thread_info(THREAD_IDENTIFIER_INFO)";
+ } else {
+ thread.id = identifier_info.thread_id;
+
+ // thread_identifier_info::thread_handle contains the base of the
+ // thread-specific data area, which on x86 and x86_64 is the thread’s base
+ // address of the %gs segment. 10.9.2 xnu-2422.90.20/osfmk/kern/thread.c
+ // thread_info_internal() gets the value from
+ // machine_thread::cthread_self, which is the same value used to set the
+ // %gs base in xnu-2422.90.20/osfmk/i386/pcb_native.c
+ // act_machine_switch_pcb().
+ //
+ // This address is the internal pthread’s _pthread::tsd[], an array of
+ // void* values that can be indexed by pthread_key_t values.
+ thread.thread_specific_data_address = identifier_info.thread_handle;
+ }
+
+ thread_precedence_policy precedence;
+ count = THREAD_PRECEDENCE_POLICY_COUNT;
+ boolean_t get_default = FALSE;
+ kr = thread_policy_get(thread.port,
+ THREAD_PRECEDENCE_POLICY,
+ reinterpret_cast<thread_policy_t>(&precedence),
+ &count,
+ &get_default);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(INFO, kr) << "thread_policy_get";
+ } else {
+ thread.priority = precedence.importance;
+ }
+
+#if defined(ARCH_CPU_X86_FAMILY)
+ mach_vm_address_t stack_pointer = Is64Bit()
+ ? thread.thread_context.t64.__rsp
+ : thread.thread_context.t32.__esp;
+#endif
+
+ thread.stack_region_address =
+ CalculateStackRegion(stack_pointer, &thread.stack_region_size);
+
+ threads_.push_back(thread);
+ }
+
+ threads_need_owners.Disarm();
+}
+
+void ProcessReader::InitializeModules() {
+ DCHECK(!initialized_modules_);
+ DCHECK(modules_.empty());
+
+ initialized_modules_ = true;
+
+ task_dyld_info_data_t dyld_info;
+ mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+ kern_return_t kr = task_info(
+ task_, TASK_DYLD_INFO, reinterpret_cast<task_info_t>(&dyld_info), &count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(WARNING, kr) << "task_info";
+ return;
+ }
+
+ // TODO(mark): Deal with statically linked executables which don’t use dyld.
+ // This may look for the module that matches the executable path in the same
+ // data set that vmmap uses.
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
+ // The task_dyld_info_data_t struct grew in 10.7, adding the format field.
+ // Don’t check this field if it’s not present, which can happen when either
+ // the SDK used at compile time or the kernel at run time are too old and
+ // don’t know about it.
+ if (count >= TASK_DYLD_INFO_COUNT) {
+ const integer_t kExpectedFormat =
+ !Is64Bit() ? TASK_DYLD_ALL_IMAGE_INFO_32 : TASK_DYLD_ALL_IMAGE_INFO_64;
+ if (dyld_info.all_image_info_format != kExpectedFormat) {
+ LOG(WARNING) << "unexpected task_dyld_info_data_t::all_image_info_format "
+ << dyld_info.all_image_info_format;
+ DCHECK_EQ(dyld_info.all_image_info_format, kExpectedFormat);
+ return;
+ }
+ }
+#endif
+
+ process_types::dyld_all_image_infos all_image_infos;
+ if (!all_image_infos.Read(this, dyld_info.all_image_info_addr)) {
+ LOG(WARNING) << "could not read dyld_all_image_infos";
+ return;
+ }
+
+ DCHECK_GE(all_image_infos.version, 1u);
+
+ // Note that all_image_infos.infoArrayCount may be 0 if a crash occurred while
+ // dyld was loading the executable. This can happen if a required dynamic
+ // library was not found. Similarly, all_image_infos.infoArray may be nullptr
+ // if a crash occurred while dyld was updating it.
+ //
+ // TODO(mark): It may be possible to recover from these situations by looking
+ // through memory mappings for Mach-O images.
+ //
+ // Continue along when this situation is detected, because even without any
+ // images in infoArray, dyldImageLoadAddress may be set, and it may be
+ // possible to recover some information from dyld.
+ if (all_image_infos.infoArrayCount == 0) {
+ LOG(WARNING) << "all_image_infos.infoArrayCount is zero";
+ } else if (!all_image_infos.infoArray) {
+ LOG(WARNING) << "all_image_infos.infoArray is nullptr";
+ }
+
+ std::vector<process_types::dyld_image_info> image_info_vector(
+ all_image_infos.infoArrayCount);
+ if (!process_types::dyld_image_info::ReadArrayInto(this,
+ all_image_infos.infoArray,
+ image_info_vector.size(),
+ &image_info_vector[0])) {
+ LOG(WARNING) << "could not read dyld_image_info array";
+ return;
+ }
+
+ size_t main_executable_count = 0;
+ bool found_dyld = false;
+ modules_.reserve(image_info_vector.size());
+ for (const process_types::dyld_image_info& image_info : image_info_vector) {
+ Module module;
+ module.timestamp = image_info.imageFileModDate;
+
+ if (!task_memory_->ReadCString(image_info.imageFilePath, &module.name)) {
+ LOG(WARNING) << "could not read dyld_image_info::imageFilePath";
+ // Proceed anyway with an empty module name.
+ }
+
+ scoped_ptr<MachOImageReader> reader(new MachOImageReader());
+ if (!reader->Initialize(this, image_info.imageLoadAddress, module.name)) {
+ reader.reset();
+ }
+
+ module.reader = reader.get();
+
+ uint32_t file_type = reader ? reader->FileType() : 0;
+
+ module_readers_.push_back(reader.release());
+ modules_.push_back(module);
+
+ if (all_image_infos.version >= 2 && all_image_infos.dyldImageLoadAddress &&
+ image_info.imageLoadAddress == all_image_infos.dyldImageLoadAddress) {
+ found_dyld = true;
+
+ LOG_IF(WARNING, file_type != MH_DYLINKER)
+ << base::StringPrintf("dylinker (%s) has unexpected Mach-O type %d",
+ module.name.c_str(),
+ file_type);
+ }
+
+ if (file_type == MH_EXECUTE) {
+ // On Mac OS X 10.6, the main executable does not normally show up at
+ // index 0. This is because of how 10.6.8 dyld-132.13/src/dyld.cpp
+ // notifyGDB(), the function resposible for causing
+ // dyld_all_image_infos::infoArray to be updated, is called. It is
+ // registered to be called when all dependents of an image have been
+ // mapped (dyld_image_state_dependents_mapped), meaning that the main
+ // executable won’t be added to the list until all of the libraries it
+ // depends on are, even though dyld begins looking at the main executable
+ // first. This changed in later versions of dyld, including those present
+ // in 10.7. 10.9.4 dyld-239.4/src/dyld.cpp updateAllImages() (renamed from
+ // notifyGDB()) is registered to be called when an image itself has been
+ // mapped (dyld_image_state_mapped), regardless of the libraries that it
+ // depends on.
+ //
+ // The interface requires that the main executable be first in the list,
+ // so swap it into the right position.
+ size_t index = modules_.size() - 1;
+ if (main_executable_count == 0) {
+ std::swap(modules_[0], modules_[index]);
+ } else {
+ LOG(WARNING) << base::StringPrintf(
+ "multiple MH_EXECUTE modules (%s, %s)",
+ modules_[0].name.c_str(),
+ modules_[index].name.c_str());
+ }
+ ++main_executable_count;
+ }
+ }
+
+ LOG_IF(WARNING, main_executable_count == 0) << "no MH_EXECUTE modules";
+
+ // all_image_infos.infoArray doesn’t include an entry for dyld, but dyld is
+ // loaded into the process’ address space as a module. Its load address is
+ // easily known given a sufficiently recent all_image_infos.version, but the
+ // timestamp and pathname are not given as they are for other modules.
+ //
+ // The timestamp is a lost cause, because the kernel doesn’t record the
+ // timestamp of the dynamic linker at the time it’s loaded in the same way
+ // that dyld records the timestamps of other modules when they’re loaded. (The
+ // timestamp for the main executable is also not reported and appears as 0
+ // even when accessed via dyld APIs, because it’s loaded by the kernel, not by
+ // dyld.)
+ //
+ // The name can be determined, but it’s not as simple as hardcoding the
+ // default "/usr/lib/dyld" because an executable could have specified anything
+ // in its LC_LOAD_DYLINKER command.
+ if (!found_dyld && all_image_infos.version >= 2 &&
+ all_image_infos.dyldImageLoadAddress) {
+ Module module;
+ module.timestamp = 0;
+
+ // Examine the executable’s LC_LOAD_DYLINKER load command to find the path
+ // used to load dyld.
+ if (all_image_infos.infoArrayCount >= 1 && main_executable_count >= 1) {
+ module.name = modules_[0].reader->DylinkerName();
+ }
+ std::string module_name = !module.name.empty() ? module.name : "(dyld)";
+
+ scoped_ptr<MachOImageReader> reader(new MachOImageReader());
+ if (!reader->Initialize(
+ this, all_image_infos.dyldImageLoadAddress, module_name)) {
+ reader.reset();
+ }
+
+ module.reader = reader.get();
+
+ uint32_t file_type = reader ? reader->FileType() : 0;
+
+ LOG_IF(WARNING, file_type != MH_DYLINKER)
+ << base::StringPrintf("dylinker (%s) has unexpected Mach-O type %d",
+ module.name.c_str(),
+ file_type);
+
+ if (module.name.empty() && file_type == MH_DYLINKER) {
+ // Look inside dyld directly to find its preferred path.
+ module.name = reader->DylinkerName();
+ }
+
+ if (module.name.empty()) {
+ module.name = "(dyld)";
+ }
+
+ // dyld is loaded in the process even if its path can’t be determined.
+ module_readers_.push_back(reader.release());
+ modules_.push_back(module);
+ }
+}
+
+mach_vm_address_t ProcessReader::CalculateStackRegion(
+ mach_vm_address_t stack_pointer,
+ mach_vm_size_t* stack_region_size) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ // For pthreads, it may be possible to compute the stack region based on the
+ // internal _pthread::stackaddr and _pthread::stacksize. The _pthread struct
+ // for a thread can be located at TSD slot 0, or the known offsets of
+ // stackaddr and stacksize from the TSD area could be used.
+ mach_vm_address_t region_base = stack_pointer;
+ mach_vm_size_t region_size;
+ natural_t depth = 0;
+ vm_prot_t protection;
+ unsigned int user_tag;
+ kern_return_t kr = MachVMRegionRecurseDeepest(
+ task_, &region_base, &region_size, &depth, &protection, &user_tag);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(INFO, kr) << "mach_vm_region_recurse";
+ *stack_region_size = 0;
+ return 0;
+ }
+
+ if (region_base > stack_pointer) {
+ // There’s nothing mapped at the stack pointer’s address. Something may have
+ // trashed the stack pointer. Note that this shouldn’t happen for a normal
+ // stack guard region violation because the guard region is mapped but has
+ // VM_PROT_NONE protection.
+ *stack_region_size = 0;
+ return 0;
+ }
+
+ mach_vm_address_t start_address = stack_pointer;
+
+ if ((protection & VM_PROT_READ) == 0) {
+ // If the region isn’t readable, the stack pointer probably points to the
+ // guard region. Don’t include it as part of the stack, and don’t include
+ // anything at any lower memory address. The code below may still possibly
+ // find the real stack region at a memory address higher than this region.
+ start_address = region_base + region_size;
+ } else {
+ // If the ABI requires a red zone, adjust the region to include it if
+ // possible.
+ LocateRedZone(&start_address, &region_base, &region_size, user_tag);
+
+ // Regardless of whether the ABI requires a red zone, capture up to
+ // kExtraCaptureSize additional bytes of stack, but only if present in the
+ // region that was already found.
+ const mach_vm_size_t kExtraCaptureSize = 128;
+ start_address = std::max(start_address >= kExtraCaptureSize
+ ? start_address - kExtraCaptureSize
+ : start_address,
+ region_base);
+
+ // Align start_address to a 16-byte boundary, which can help readers by
+ // ensuring that data is aligned properly. This could page-align instead,
+ // but that might be wasteful.
+ const mach_vm_size_t kDesiredAlignment = 16;
+ start_address &= ~(kDesiredAlignment - 1);
+ DCHECK_GE(start_address, region_base);
+ }
+
+ region_size -= (start_address - region_base);
+ region_base = start_address;
+
+ mach_vm_size_t total_region_size = region_size;
+
+ // The stack region may have gotten split up into multiple abutting regions.
+ // Try to coalesce them. This frequently happens for the main thread’s stack
+ // when setrlimit(RLIMIT_STACK, …) is called. It may also happen if a region
+ // is split up due to an mprotect() or vm_protect() call.
+ //
+ // Stack regions created by the kernel and the pthreads library will be marked
+ // with the VM_MEMORY_STACK user tag. Scanning for multiple adjacent regions
+ // with the same tag should find an entire stack region. Checking that the
+ // protection on individual regions is not VM_PROT_NONE should guarantee that
+ // this algorithm doesn’t collect map entries belonging to another thread’s
+ // stack: well-behaved stacks (such as those created by the kernel and the
+ // pthreads library) have VM_PROT_NONE guard regions at their low-address
+ // ends.
+ //
+ // Other stack regions may not be so well-behaved and thus if user_tag is not
+ // VM_MEMORY_STACK, the single region that was found is used as-is without
+ // trying to merge it with other adjacent regions.
+ if (user_tag == VM_MEMORY_STACK) {
+ mach_vm_address_t try_address = region_base;
+ mach_vm_address_t original_try_address;
+
+ while (try_address += region_size,
+ original_try_address = try_address,
+ (kr = MachVMRegionRecurseDeepest(task_,
+ &try_address,
+ &region_size,
+ &depth,
+ &protection,
+ &user_tag) == KERN_SUCCESS) &&
+ try_address == original_try_address &&
+ (protection & VM_PROT_READ) != 0 &&
+ user_tag == VM_MEMORY_STACK) {
+ total_region_size += region_size;
+ }
+
+ if (kr != KERN_SUCCESS && kr != KERN_INVALID_ADDRESS) {
+ // Tolerate KERN_INVALID_ADDRESS because it will be returned when there
+ // are no more regions in the map at or above the specified |try_address|.
+ MACH_LOG(INFO, kr) << "mach_vm_region_recurse";
+ }
+ }
+
+ *stack_region_size = total_region_size;
+ return region_base;
+}
+
+void ProcessReader::LocateRedZone(mach_vm_address_t* const start_address,
+ mach_vm_address_t* const region_base,
+ mach_vm_address_t* const region_size,
+ const unsigned int user_tag) {
+#if defined(ARCH_CPU_X86_FAMILY)
+ if (Is64Bit()) {
+ // x86_64 has a red zone. See AMD64 ABI 0.99.6,
+ // http://www.x86-64.org/documentation/abi.pdf, section 3.2.2, “The Stack
+ // Frame”.
+ const mach_vm_size_t kRedZoneSize = 128;
+ mach_vm_address_t red_zone_base =
+ *start_address >= kRedZoneSize ? *start_address - kRedZoneSize : 0;
+ bool red_zone_ok = false;
+ if (red_zone_base >= *region_base) {
+ // The red zone is within the region already discovered.
+ red_zone_ok = true;
+ } else if (red_zone_base < *region_base && user_tag == VM_MEMORY_STACK) {
+ // Probe to see if there’s a region immediately below the one already
+ // discovered.
+ mach_vm_address_t red_zone_region_base = red_zone_base;
+ mach_vm_size_t red_zone_region_size;
+ natural_t red_zone_depth = 0;
+ vm_prot_t red_zone_protection;
+ unsigned int red_zone_user_tag;
+ kern_return_t kr = MachVMRegionRecurseDeepest(task_,
+ &red_zone_region_base,
+ &red_zone_region_size,
+ &red_zone_depth,
+ &red_zone_protection,
+ &red_zone_user_tag);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(INFO, kr) << "mach_vm_region_recurse";
+ *start_address = *region_base;
+ } else if (red_zone_region_base + red_zone_region_size == *region_base &&
+ (red_zone_protection & VM_PROT_READ) != 0 &&
+ red_zone_user_tag == user_tag) {
+ // The region containing the red zone is immediately below the region
+ // already found, it’s readable (not the guard region), and it has the
+ // same user tag as the region already found, so merge them.
+ red_zone_ok = true;
+ *region_base -= red_zone_region_size;
+ *region_size += red_zone_region_size;
+ }
+ }
+
+ if (red_zone_ok) {
+ // Begin capturing from the base of the red zone (but not the entire
+ // region that encompasses the red zone).
+ *start_address = red_zone_base;
+ } else {
+ // The red zone would go lower into another region in memory, but no
+ // region was found. Memory can only be captured to an address as low as
+ // the base address of the region already found.
+ *start_address = *region_base;
+ }
+ }
+#endif
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/process_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_reader.h
new file mode 100644
index 00000000000..a781667f282
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_reader.h
@@ -0,0 +1,239 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_H_
+#define CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_H_
+
+#include <mach/mach.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "build/build_config.h"
+#include "util/mach/task_memory.h"
+#include "util/misc/initialization_state_dcheck.h"
+#include "util/posix/process_info.h"
+#include "util/stdlib/pointer_container.h"
+
+namespace crashpad {
+
+class MachOImageReader;
+
+//! \brief Accesses information about another process, identified by a Mach
+//! task.
+class ProcessReader {
+ public:
+ //! \brief Contains information about a thread that belongs to a task
+ //! (process).
+ struct Thread {
+#if defined(ARCH_CPU_X86_FAMILY)
+ union ThreadContext {
+ x86_thread_state64_t t64;
+ x86_thread_state32_t t32;
+ };
+ union FloatContext {
+ x86_float_state64_t f64;
+ x86_float_state32_t f32;
+ };
+ union DebugContext {
+ x86_debug_state64_t d64;
+ x86_debug_state32_t d32;
+ };
+#endif
+
+ Thread();
+ ~Thread() {}
+
+ ThreadContext thread_context;
+ FloatContext float_context;
+ DebugContext debug_context;
+ uint64_t id;
+ mach_vm_address_t stack_region_address;
+ mach_vm_size_t stack_region_size;
+ mach_vm_address_t thread_specific_data_address;
+ thread_t port;
+ int suspend_count;
+ int priority;
+ };
+
+ //! \brief Contains information about a module loaded into a process.
+ struct Module {
+ Module();
+ ~Module();
+
+ //! \brief The pathname used to load the module from disk.
+ std::string name;
+
+ //! \brief An image reader for the module.
+ //!
+ //! The lifetime of this MachOImageReader is scoped to the lifetime of the
+ //! ProcessReader that created it.
+ //!
+ //! This field may be `nullptr` if a reader could not be created for the
+ //! module.
+ const MachOImageReader* reader;
+
+ //! \brief The module’s timestamp.
+ //!
+ //! This field will be `0` if its value cannot be determined. It can only be
+ //! determined for images that are loaded by dyld, so it will be `0` for the
+ //! main executable and for dyld itself.
+ time_t timestamp;
+ };
+
+ ProcessReader();
+ ~ProcessReader();
+
+ //! \brief Initializes this object. This method must be called before any
+ //! other.
+ //!
+ //! \param[in] task A send right to the target task’s task port. This object
+ //! does not take ownership of the send right.
+ //!
+ //! \return `true` on success, indicating that this object will respond
+ //! validly to further method calls. `false` on failure. On failure, no
+ //! further method calls should be made.
+ bool Initialize(task_t task);
+
+ //! \return `true` if the target task is a 64-bit process.
+ bool Is64Bit() const { return is_64_bit_; }
+
+ //! \return The target task’s process ID.
+ pid_t ProcessID() const { return process_info_.ProcessID(); }
+
+ //! \return The target task’s parent process ID.
+ pid_t ParentProcessID() const { return process_info_.ParentProcessID(); }
+
+ //! \brief Determines the target process’ start time.
+ //!
+ //! \param[out] start_time The time that the process started.
+ void StartTime(timeval* start_time) const {
+ process_info_.StartTime(start_time);
+ }
+
+ //! \brief Determines the target process’ execution time.
+ //!
+ //! \param[out] user_time The amount of time the process has executed code in
+ //! user mode.
+ //! \param[out] system_time The amount of time the process has executed code
+ //! in system mode.
+ //!
+ //! \return `true` on success, `false` on failure, with a warning logged. On
+ //! failure, \a user_time and \a system_time will be set to represent no
+ //! time spent executing code in user or system mode.
+ bool CPUTimes(timeval* user_time, timeval* system_time) const;
+
+ //! \return Accesses the memory of the target task.
+ TaskMemory* Memory() { return task_memory_.get(); }
+
+ //! \return The threads that are in the task (process). The first element (at
+ //! index `0`) corresponds to the main thread.
+ const std::vector<Thread>& Threads();
+
+ //! \return The modules loaded in the process. The first element (at index
+ //! `0`) corresponds to the main executable, and the final element
+ //! corresponds to the dynamic loader, dyld.
+ const std::vector<Module>& Modules();
+
+ private:
+ //! Performs lazy initialization of the \a threads_ vector on behalf of
+ //! Threads().
+ void InitializeThreads();
+
+ //! Performs lazy initialization of the \a modules_ vector on behalf of
+ //! Modules().
+ void InitializeModules();
+
+ //! \brief Calculates the base address and size of the region used as a
+ //! thread’s stack.
+ //!
+ //! The region returned by this method may be formed by merging multiple
+ //! adjacent regions in a process’ memory map if appropriate. The base address
+ //! of the returned region may be lower than the \a stack_pointer passed in
+ //! when the ABI mandates a red zone below the stack pointer.
+ //!
+ //! \param[in] stack_pointer The stack pointer, referring to the top (lowest
+ //! address) of a thread’s stack.
+ //! \param[out] stack_region_size The size of the memory region used as the
+ //! thread’s stack.
+ //!
+ //! \return The base address (lowest address) of the memory region used as the
+ //! thread’s stack.
+ mach_vm_address_t CalculateStackRegion(mach_vm_address_t stack_pointer,
+ mach_vm_size_t* stack_region_size);
+
+ //! \brief Adjusts the region for the red zone, if the ABI requires one.
+ //!
+ //! This method performs red zone calculation for CalculateStackRegion(). Its
+ //! parameters are local variables used within that method, and may be
+ //! modified as needed.
+ //!
+ //! Where a red zone is required, the region of memory captured for a thread’s
+ //! stack will be extended to include the red zone below the stack pointer,
+ //! provided that such memory is mapped, readable, and has the correct user
+ //! tag value. If these conditions cannot be met fully, as much of the red
+ //! zone will be captured as is possible while meeting these conditions.
+ //!
+ //! \param[inout] start_address The base address of the region to begin
+ //! capturing stack memory from. On entry, \a start_address is the stack
+ //! pointer. On return, \a start_address may be decreased to encompass a
+ //! red zone.
+ //! \param[inout] region_base The base address of the region that contains
+ //! stack memory. This is distinct from \a start_address in that \a
+ //! region_base will be page-aligned. On entry, \a region_base is the
+ //! base address of a region that contains \a start_address. On return,
+ //! if \a start_address is decremented and is outside of the region
+ //! originally described by \a region_base, \a region_base will also be
+ //! decremented appropriately.
+ //! \param[inout] region_size The size of the region that contains stack
+ //! memory. This region begins at \a region_base. On return, if \a
+ //! region_base is decremented, \a region_size will be incremented
+ //! appropriately.
+ //! \param[in] user_tag The Mach VM system’s user tag for the region described
+ //! by the initial values of \a region_base and \a region_size. The red
+ //! zone will only be allowed to extend out of the region described by
+ //! these initial values if the user tag is appropriate for stack memory
+ //! and the expanded region has the same user tag value.
+ void LocateRedZone(mach_vm_address_t* start_address,
+ mach_vm_address_t* region_base,
+ mach_vm_address_t* region_size,
+ unsigned int user_tag);
+
+ ProcessInfo process_info_;
+ std::vector<Thread> threads_; // owns send rights
+ std::vector<Module> modules_;
+ PointerVector<MachOImageReader> module_readers_;
+ scoped_ptr<TaskMemory> task_memory_;
+ task_t task_; // weak
+ InitializationStateDcheck initialized_;
+
+ // This shadows a method of process_info_, but it’s accessed so frequently
+ // that it’s given a first-class field to save a call and a few bit operations
+ // on each access.
+ bool is_64_bit_;
+
+ bool initialized_threads_;
+ bool initialized_modules_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessReader);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/process_reader_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_reader_test.cc
new file mode 100644
index 00000000000..25160d104cb
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_reader_test.cc
@@ -0,0 +1,829 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/mac/process_reader.h"
+
+#include <AvailabilityMacros.h>
+#include <mach-o/dyld.h>
+#include <mach-o/dyld_images.h>
+#include <mach/mach.h>
+#include <OpenCL/opencl.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/stringprintf.h"
+#include "build/build_config.h"
+#include "gtest/gtest.h"
+#include "snapshot/mac/mach_o_image_reader.h"
+#include "test/errors.h"
+#include "test/mac/dyld.h"
+#include "test/mac/mach_errors.h"
+#include "test/mac/mach_multiprocess.h"
+#include "util/file/file_io.h"
+#include "util/mac/mac_util.h"
+#include "util/mach/mach_extensions.h"
+#include "util/stdlib/pointer_container.h"
+#include "util/synchronization/semaphore.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(ProcessReader, SelfBasic) {
+ ProcessReader process_reader;
+ ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
+
+#if !defined(ARCH_CPU_64_BITS)
+ EXPECT_FALSE(process_reader.Is64Bit());
+#else
+ EXPECT_TRUE(process_reader.Is64Bit());
+#endif
+
+ EXPECT_EQ(getpid(), process_reader.ProcessID());
+ EXPECT_EQ(getppid(), process_reader.ParentProcessID());
+
+ const char kTestMemory[] = "Some test memory";
+ char buffer[arraysize(kTestMemory)];
+ ASSERT_TRUE(process_reader.Memory()->Read(
+ reinterpret_cast<mach_vm_address_t>(kTestMemory),
+ sizeof(kTestMemory),
+ &buffer));
+ EXPECT_STREQ(kTestMemory, buffer);
+}
+
+const char kTestMemory[] = "Read me from another process";
+
+class ProcessReaderChild final : public MachMultiprocess {
+ public:
+ ProcessReaderChild() : MachMultiprocess() {}
+
+ ~ProcessReaderChild() {}
+
+ private:
+ void MachMultiprocessParent() override {
+ ProcessReader process_reader;
+ ASSERT_TRUE(process_reader.Initialize(ChildTask()));
+
+#if !defined(ARCH_CPU_64_BITS)
+ EXPECT_FALSE(process_reader.Is64Bit());
+#else
+ EXPECT_TRUE(process_reader.Is64Bit());
+#endif
+
+ EXPECT_EQ(getpid(), process_reader.ParentProcessID());
+ EXPECT_EQ(ChildPID(), process_reader.ProcessID());
+
+ FileHandle read_handle = ReadPipeHandle();
+
+ mach_vm_address_t address;
+ CheckedReadFile(read_handle, &address, sizeof(address));
+
+ std::string read_string;
+ ASSERT_TRUE(process_reader.Memory()->ReadCString(address, &read_string));
+ EXPECT_EQ(kTestMemory, read_string);
+ }
+
+ void MachMultiprocessChild() override {
+ FileHandle write_handle = WritePipeHandle();
+
+ mach_vm_address_t address =
+ reinterpret_cast<mach_vm_address_t>(kTestMemory);
+ CheckedWriteFile(write_handle, &address, sizeof(address));
+
+ // Wait for the parent to signal that it’s OK to exit by closing its end of
+ // the pipe.
+ CheckedReadFileAtEOF(ReadPipeHandle());
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessReaderChild);
+};
+
+TEST(ProcessReader, ChildBasic) {
+ ProcessReaderChild process_reader_child;
+ process_reader_child.Run();
+}
+
+// Returns a thread ID given a pthread_t. This wraps pthread_threadid_np() but
+// that function has a cumbersome interface because it returns a success value.
+// This function CHECKs success and returns the thread ID directly.
+uint64_t PthreadToThreadID(pthread_t pthread) {
+ uint64_t thread_id;
+ int rv = pthread_threadid_np(pthread, &thread_id);
+ CHECK_EQ(rv, 0);
+ return thread_id;
+}
+
+TEST(ProcessReader, SelfOneThread) {
+ ProcessReader process_reader;
+ ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
+
+ const std::vector<ProcessReader::Thread>& threads = process_reader.Threads();
+
+ // If other tests ran in this process previously, threads may have been
+ // created and may still be running. This check must look for at least one
+ // thread, not exactly one thread.
+ ASSERT_GE(threads.size(), 1u);
+
+ EXPECT_EQ(PthreadToThreadID(pthread_self()), threads[0].id);
+
+ thread_t thread_self = MachThreadSelf();
+ EXPECT_EQ(thread_self, threads[0].port);
+
+ EXPECT_EQ(0, threads[0].suspend_count);
+}
+
+class TestThreadPool {
+ public:
+ struct ThreadExpectation {
+ mach_vm_address_t stack_address;
+ int suspend_count;
+ };
+
+ TestThreadPool() : thread_infos_() {
+ }
+
+ // Resumes suspended threads, signals each thread’s exit semaphore asking it
+ // to exit, and joins each thread, blocking until they have all exited.
+ ~TestThreadPool() {
+ for (ThreadInfo* thread_info : thread_infos_) {
+ thread_t thread_port = pthread_mach_thread_np(thread_info->pthread);
+ while (thread_info->suspend_count > 0) {
+ kern_return_t kr = thread_resume(thread_port);
+ EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "thread_resume");
+ --thread_info->suspend_count;
+ }
+ }
+
+ for (ThreadInfo* thread_info : thread_infos_) {
+ thread_info->exit_semaphore.Signal();
+ }
+
+ for (const ThreadInfo* thread_info : thread_infos_) {
+ int rv = pthread_join(thread_info->pthread, nullptr);
+ CHECK_EQ(0, rv);
+ }
+ }
+
+ // Starts |thread_count| threads and waits on each thread’s ready semaphore,
+ // so that when this function returns, all threads have been started and have
+ // all run to the point that they’ve signalled that they are ready.
+ void StartThreads(size_t thread_count) {
+ ASSERT_TRUE(thread_infos_.empty());
+
+ for (size_t thread_index = 0; thread_index < thread_count; ++thread_index) {
+ ThreadInfo* thread_info = new ThreadInfo();
+ thread_infos_.push_back(thread_info);
+
+ int rv = pthread_create(&thread_info->pthread,
+ nullptr,
+ ThreadMain,
+ thread_info);
+ ASSERT_EQ(0, rv);
+ }
+
+ for (ThreadInfo* thread_info : thread_infos_) {
+ thread_info->ready_semaphore.Wait();
+ }
+
+ // If present, suspend the thread at indices 1 through 3 the same number of
+ // times as their index. This tests reporting of suspend counts.
+ for (size_t thread_index = 1;
+ thread_index < thread_infos_.size() && thread_index < 4;
+ ++thread_index) {
+ thread_t thread_port =
+ pthread_mach_thread_np(thread_infos_[thread_index]->pthread);
+ for (size_t suspend_count = 0;
+ suspend_count < thread_index;
+ ++suspend_count) {
+ kern_return_t kr = thread_suspend(thread_port);
+ EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "thread_suspend");
+ if (kr == KERN_SUCCESS) {
+ ++thread_infos_[thread_index]->suspend_count;
+ }
+ }
+ }
+ }
+
+ uint64_t GetThreadInfo(size_t thread_index,
+ ThreadExpectation* expectation) {
+ CHECK_LT(thread_index, thread_infos_.size());
+
+ const ThreadInfo* thread_info = thread_infos_[thread_index];
+ expectation->stack_address = thread_info->stack_address;
+ expectation->suspend_count = thread_info->suspend_count;
+
+ return PthreadToThreadID(thread_info->pthread);
+ }
+
+ private:
+ struct ThreadInfo {
+ ThreadInfo()
+ : pthread(nullptr),
+ stack_address(0),
+ ready_semaphore(0),
+ exit_semaphore(0),
+ suspend_count(0) {
+ }
+
+ ~ThreadInfo() {}
+
+ // The thread’s ID, set at the time the thread is created.
+ pthread_t pthread;
+
+ // An address somewhere within the thread’s stack. The thread sets this in
+ // its ThreadMain().
+ mach_vm_address_t stack_address;
+
+ // The worker thread signals ready_semaphore to indicate that it’s done
+ // setting up its ThreadInfo structure. The main thread waits on this
+ // semaphore before using any data that the worker thread is responsible for
+ // setting.
+ Semaphore ready_semaphore;
+
+ // The worker thread waits on exit_semaphore to determine when it’s safe to
+ // exit. The main thread signals exit_semaphore when it no longer needs the
+ // worker thread.
+ Semaphore exit_semaphore;
+
+ // The thread’s suspend count.
+ int suspend_count;
+ };
+
+ static void* ThreadMain(void* argument) {
+ ThreadInfo* thread_info = static_cast<ThreadInfo*>(argument);
+
+ thread_info->stack_address =
+ reinterpret_cast<mach_vm_address_t>(&thread_info);
+
+ thread_info->ready_semaphore.Signal();
+ thread_info->exit_semaphore.Wait();
+
+ // Check this here after everything’s known to be synchronized, otherwise
+ // there’s a race between the parent thread storing this thread’s pthread_t
+ // in thread_info_pthread and this thread starting and attempting to access
+ // it.
+ CHECK_EQ(pthread_self(), thread_info->pthread);
+
+ return nullptr;
+ }
+
+ // This is a PointerVector because the address of a ThreadInfo object is
+ // passed to each thread’s ThreadMain(), so they cannot move around in memory.
+ PointerVector<ThreadInfo> thread_infos_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestThreadPool);
+};
+
+using ThreadMap = std::map<uint64_t, TestThreadPool::ThreadExpectation>;
+
+// Verifies that all of the threads in |threads|, obtained from ProcessReader,
+// agree with the expectation in |thread_map|. If |tolerate_extra_threads| is
+// true, |threads| is allowed to contain threads that are not listed in
+// |thread_map|. This is useful when testing situations where code outside of
+// the test’s control (such as system libraries) may start threads, or may have
+// started threads prior to a test’s execution.
+void ExpectSeveralThreads(ThreadMap* thread_map,
+ const std::vector<ProcessReader::Thread>& threads,
+ const bool tolerate_extra_threads) {
+ if (tolerate_extra_threads) {
+ ASSERT_GE(threads.size(), thread_map->size());
+ } else {
+ ASSERT_EQ(thread_map->size(), threads.size());
+ }
+
+ for (size_t thread_index = 0; thread_index < threads.size(); ++thread_index) {
+ const ProcessReader::Thread& thread = threads[thread_index];
+ mach_vm_address_t thread_stack_region_end =
+ thread.stack_region_address + thread.stack_region_size;
+
+ const auto& iterator = thread_map->find(thread.id);
+ if (!tolerate_extra_threads) {
+ // Make sure that the thread is in the expectation map.
+ ASSERT_NE(thread_map->end(), iterator);
+ }
+
+ if (iterator != thread_map->end()) {
+ EXPECT_GE(iterator->second.stack_address, thread.stack_region_address);
+ EXPECT_LT(iterator->second.stack_address, thread_stack_region_end);
+
+ EXPECT_EQ(iterator->second.suspend_count, thread.suspend_count);
+
+ // Remove the thread from the expectation map since it’s already been
+ // found. This makes it easy to check for duplicate thread IDs, and makes
+ // it easy to check that all expected threads were found.
+ thread_map->erase(iterator);
+ }
+
+ // Make sure that this thread’s ID, stack region, and port don’t conflict
+ // with any other thread’s. Each thread should have a unique value for its
+ // ID and port, and each should have its own stack that doesn’t touch any
+ // other thread’s stack.
+ for (size_t other_thread_index = 0;
+ other_thread_index < threads.size();
+ ++other_thread_index) {
+ if (thread_index == other_thread_index) {
+ continue;
+ }
+
+ const ProcessReader::Thread& other_thread = threads[other_thread_index];
+
+ EXPECT_NE(thread.id, other_thread.id);
+ EXPECT_NE(thread.port, other_thread.port);
+
+ mach_vm_address_t other_thread_stack_region_end =
+ other_thread.stack_region_address + other_thread.stack_region_size;
+ EXPECT_FALSE(
+ thread.stack_region_address >= other_thread.stack_region_address &&
+ thread.stack_region_address < other_thread_stack_region_end);
+ EXPECT_FALSE(
+ thread_stack_region_end > other_thread.stack_region_address &&
+ thread_stack_region_end <= other_thread_stack_region_end);
+ }
+ }
+
+ // Make sure that each expected thread was found.
+ EXPECT_TRUE(thread_map->empty());
+}
+
+TEST(ProcessReader, SelfSeveralThreads) {
+ // Set up the ProcessReader here, before any other threads are running. This
+ // tests that the threads it returns are lazily initialized as a snapshot of
+ // the threads at the time of the first call to Threads(), and not at the
+ // time the ProcessReader was created or initialized.
+ ProcessReader process_reader;
+ ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
+
+ TestThreadPool thread_pool;
+ const size_t kChildThreads = 16;
+ ASSERT_NO_FATAL_FAILURE(thread_pool.StartThreads(kChildThreads));
+
+ // Build a map of all expected threads, keyed by each thread’s ID. The values
+ // are addresses that should lie somewhere within each thread’s stack.
+ ThreadMap thread_map;
+ const uint64_t self_thread_id = PthreadToThreadID(pthread_self());
+ TestThreadPool::ThreadExpectation expectation;
+ expectation.stack_address = reinterpret_cast<mach_vm_address_t>(&thread_map);
+ expectation.suspend_count = 0;
+ thread_map[self_thread_id] = expectation;
+ for (size_t thread_index = 0; thread_index < kChildThreads; ++thread_index) {
+ uint64_t thread_id = thread_pool.GetThreadInfo(thread_index, &expectation);
+
+ // There can’t be any duplicate thread IDs.
+ EXPECT_EQ(0u, thread_map.count(thread_id));
+
+ thread_map[thread_id] = expectation;
+ }
+
+ const std::vector<ProcessReader::Thread>& threads = process_reader.Threads();
+
+ // Other tests that have run previously may have resulted in the creation of
+ // threads that still exist, so pass true for |tolerate_extra_threads|.
+ ExpectSeveralThreads(&thread_map, threads, true);
+
+ // When testing in-process, verify that when this thread shows up in the
+ // vector, it has the expected thread port, and that this thread port only
+ // shows up once.
+ thread_t thread_self = MachThreadSelf();
+ bool found_thread_self = false;
+ for (const ProcessReader::Thread& thread : threads) {
+ if (thread.port == thread_self) {
+ EXPECT_FALSE(found_thread_self);
+ found_thread_self = true;
+ EXPECT_EQ(self_thread_id, thread.id);
+ }
+ }
+ EXPECT_TRUE(found_thread_self);
+}
+
+class ProcessReaderThreadedChild final : public MachMultiprocess {
+ public:
+ explicit ProcessReaderThreadedChild(size_t thread_count)
+ : MachMultiprocess(),
+ thread_count_(thread_count) {
+ }
+
+ ~ProcessReaderThreadedChild() {}
+
+ private:
+ void MachMultiprocessParent() override {
+ ProcessReader process_reader;
+ ASSERT_TRUE(process_reader.Initialize(ChildTask()));
+
+ FileHandle read_handle = ReadPipeHandle();
+
+ // Build a map of all expected threads, keyed by each thread’s ID, and with
+ // addresses that should lie somewhere within each thread’s stack as values.
+ // These IDs and addresses all come from the child process via the pipe.
+ ThreadMap thread_map;
+ for (size_t thread_index = 0;
+ thread_index < thread_count_ + 1;
+ ++thread_index) {
+ uint64_t thread_id;
+ CheckedReadFile(read_handle, &thread_id, sizeof(thread_id));
+
+ TestThreadPool::ThreadExpectation expectation;
+ CheckedReadFile(read_handle,
+ &expectation.stack_address,
+ sizeof(expectation.stack_address));
+ CheckedReadFile(read_handle,
+ &expectation.suspend_count,
+ sizeof(expectation.suspend_count));
+
+ // There can’t be any duplicate thread IDs.
+ EXPECT_EQ(0u, thread_map.count(thread_id));
+
+ thread_map[thread_id] = expectation;
+ }
+
+ const std::vector<ProcessReader::Thread>& threads = process_reader.Threads();
+
+ // The child shouldn’t have any threads other than its main thread and the
+ // ones it created in its pool, so pass false for |tolerate_extra_threads|.
+ ExpectSeveralThreads(&thread_map, threads, false);
+ }
+
+ void MachMultiprocessChild() override {
+ TestThreadPool thread_pool;
+ ASSERT_NO_FATAL_FAILURE(thread_pool.StartThreads(thread_count_));
+
+ FileHandle write_handle = WritePipeHandle();
+
+ // This thread isn’t part of the thread pool, but the parent will be able
+ // to inspect it. Write an entry for it.
+ uint64_t thread_id = PthreadToThreadID(pthread_self());
+
+ CheckedWriteFile(write_handle, &thread_id, sizeof(thread_id));
+
+ TestThreadPool::ThreadExpectation expectation;
+ expectation.stack_address = reinterpret_cast<mach_vm_address_t>(&thread_id);
+ expectation.suspend_count = 0;
+
+ CheckedWriteFile(write_handle,
+ &expectation.stack_address,
+ sizeof(expectation.stack_address));
+ CheckedWriteFile(write_handle,
+ &expectation.suspend_count,
+ sizeof(expectation.suspend_count));
+
+ // Write an entry for everything in the thread pool.
+ for (size_t thread_index = 0;
+ thread_index < thread_count_;
+ ++thread_index) {
+ uint64_t thread_id =
+ thread_pool.GetThreadInfo(thread_index, &expectation);
+
+ CheckedWriteFile(write_handle, &thread_id, sizeof(thread_id));
+ CheckedWriteFile(write_handle,
+ &expectation.stack_address,
+ sizeof(expectation.stack_address));
+ CheckedWriteFile(write_handle,
+ &expectation.suspend_count,
+ sizeof(expectation.suspend_count));
+ }
+
+ // Wait for the parent to signal that it’s OK to exit by closing its end of
+ // the pipe.
+ CheckedReadFileAtEOF(ReadPipeHandle());
+ }
+
+ size_t thread_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessReaderThreadedChild);
+};
+
+TEST(ProcessReader, ChildOneThread) {
+ // The main thread plus zero child threads equals one thread.
+ const size_t kChildThreads = 0;
+ ProcessReaderThreadedChild process_reader_threaded_child(kChildThreads);
+ process_reader_threaded_child.Run();
+}
+
+TEST(ProcessReader, ChildSeveralThreads) {
+ const size_t kChildThreads = 64;
+ ProcessReaderThreadedChild process_reader_threaded_child(kChildThreads);
+ process_reader_threaded_child.Run();
+}
+
+// cl_kernels images (OpenCL kernels) are weird. They’re not ld output and don’t
+// exist as files on disk. On Mac OS X 10.10, their Mach-O structure isn’t
+// perfect. They show up loaded into many executables, so these quirks should be
+// tolerated.
+//
+// Create an object of this class to ensure that at least one cl_kernels image
+// is present in a process, to be able to test that all of the process-reading
+// machinery tolerates them. On systems where cl_kernels modules have known
+// quirks, the image that an object of this class produces will also have those
+// quirks.
+//
+// https://openradar.appspot.com/20239912
+class ScopedOpenCLNoOpKernel {
+ public:
+ ScopedOpenCLNoOpKernel()
+ : context_(nullptr),
+ program_(nullptr),
+ kernel_(nullptr) {
+ }
+
+ ~ScopedOpenCLNoOpKernel() {
+ if (kernel_) {
+ cl_int rv = clReleaseKernel(kernel_);
+ EXPECT_EQ(CL_SUCCESS, rv) << "clReleaseKernel";
+ }
+
+ if (program_) {
+ cl_int rv = clReleaseProgram(program_);
+ EXPECT_EQ(CL_SUCCESS, rv) << "clReleaseProgram";
+ }
+
+ if (context_) {
+ cl_int rv = clReleaseContext(context_);
+ EXPECT_EQ(CL_SUCCESS, rv) << "clReleaseContext";
+ }
+ }
+
+ void SetUp() {
+ cl_platform_id platform_id;
+ cl_int rv = clGetPlatformIDs(1, &platform_id, nullptr);
+ ASSERT_EQ(CL_SUCCESS, rv) << "clGetPlatformIDs";
+
+ // Use CL_DEVICE_TYPE_CPU to ensure that the kernel would execute on the
+ // CPU. This is the only device type that a cl_kernels image will be created
+ // for.
+ cl_device_id device_id;
+ rv =
+ clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_CPU, 1, &device_id, nullptr);
+ ASSERT_EQ(CL_SUCCESS, rv) << "clGetDeviceIDs";
+
+ context_ = clCreateContext(nullptr, 1, &device_id, nullptr, nullptr, &rv);
+ ASSERT_EQ(CL_SUCCESS, rv) << "clCreateContext";
+
+ // The goal of the program in |sources| is to produce a cl_kernels image
+ // that doesn’t strictly conform to Mach-O expectations. On Mac OS X 10.10,
+ // cl_kernels modules show up with an __LD,__compact_unwind section, showing
+ // up in the __TEXT segment. MachOImageSegmentReader would normally reject
+ // modules for this problem, but a special exception is made when this
+ // occurs in cl_kernels images. This portion of the test is aimed at making
+ // sure that this exception works correctly.
+ //
+ // A true no-op program doesn’t actually produce unwind data, so there would
+ // be no errant __LD,__compact_unwind section on 10.10, and the test
+ // wouldn’t be complete. This simple no-op, which calls a built-in function,
+ // does produce unwind data provided optimization is disabled.
+ // "-cl-opt-disable" is given to clBuildProgram() below.
+ const char* sources[] = {
+ "__kernel void NoOp(void) {barrier(CLK_LOCAL_MEM_FENCE);}",
+ };
+ const size_t source_lengths[] = {
+ strlen(sources[0]),
+ };
+ static_assert(arraysize(sources) == arraysize(source_lengths),
+ "arrays must be parallel");
+
+ program_ = clCreateProgramWithSource(
+ context_, arraysize(sources), sources, source_lengths, &rv);
+ ASSERT_EQ(CL_SUCCESS, rv) << "clCreateProgramWithSource";
+
+ rv = clBuildProgram(
+ program_, 1, &device_id, "-cl-opt-disable", nullptr, nullptr);
+ ASSERT_EQ(CL_SUCCESS, rv) << "clBuildProgram";
+
+ kernel_ = clCreateKernel(program_, "NoOp", &rv);
+ ASSERT_EQ(CL_SUCCESS, rv) << "clCreateKernel";
+ }
+
+ private:
+ cl_context context_;
+ cl_program program_;
+ cl_kernel kernel_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedOpenCLNoOpKernel);
+};
+
+// Although Mac OS X 10.6 has OpenCL and can compile and execute OpenCL code,
+// OpenCL kernels that run on the CPU do not result in cl_kernels images
+// appearing on that OS version.
+bool ExpectCLKernels() {
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7
+ return true;
+#else
+ return MacOSXMinorVersion() >= 7;
+#endif
+}
+
+TEST(ProcessReader, SelfModules) {
+ ScopedOpenCLNoOpKernel ensure_cl_kernels;
+ ASSERT_NO_FATAL_FAILURE(ensure_cl_kernels.SetUp());
+
+ ProcessReader process_reader;
+ ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
+
+ uint32_t dyld_image_count = _dyld_image_count();
+ const std::vector<ProcessReader::Module>& modules = process_reader.Modules();
+
+ // There needs to be at least an entry for the main executable, for a dylib,
+ // and for dyld.
+ ASSERT_GE(modules.size(), 3u);
+
+ // dyld_image_count doesn’t include an entry for dyld itself, but |modules|
+ // does.
+ ASSERT_EQ(dyld_image_count + 1, modules.size());
+
+ bool found_cl_kernels = false;
+ for (uint32_t index = 0; index < dyld_image_count; ++index) {
+ SCOPED_TRACE(base::StringPrintf(
+ "index %u, name %s", index, modules[index].name.c_str()));
+
+ const char* dyld_image_name = _dyld_get_image_name(index);
+ EXPECT_EQ(dyld_image_name, modules[index].name);
+ ASSERT_TRUE(modules[index].reader);
+ EXPECT_EQ(
+ reinterpret_cast<mach_vm_address_t>(_dyld_get_image_header(index)),
+ modules[index].reader->Address());
+
+ if (index == 0) {
+ // dyld didn’t load the main executable, so it couldn’t record its
+ // timestamp, and it is reported as 0.
+ EXPECT_EQ(0, modules[index].timestamp);
+ } else if (modules[index].reader->FileType() == MH_BUNDLE &&
+ modules[index].name == "cl_kernels") {
+ // cl_kernels doesn’t exist as a file.
+ EXPECT_EQ(0, modules[index].timestamp);
+ found_cl_kernels = true;
+ } else {
+ // Hope that the module didn’t change on disk.
+ struct stat stat_buf;
+ int rv = stat(dyld_image_name, &stat_buf);
+ EXPECT_EQ(0, rv) << ErrnoMessage("stat");
+ if (rv == 0) {
+ EXPECT_EQ(stat_buf.st_mtime, modules[index].timestamp);
+ }
+ }
+ }
+
+ EXPECT_EQ(ExpectCLKernels(), found_cl_kernels);
+
+ size_t index = modules.size() - 1;
+ EXPECT_EQ("/usr/lib/dyld", modules[index].name);
+
+ // dyld didn’t load itself either, so it couldn’t record its timestamp, and it
+ // is also reported as 0.
+ EXPECT_EQ(0, modules[index].timestamp);
+
+ const struct dyld_all_image_infos* dyld_image_infos =
+ _dyld_get_all_image_infos();
+ if (dyld_image_infos->version >= 2) {
+ ASSERT_TRUE(modules[index].reader);
+ EXPECT_EQ(
+ reinterpret_cast<mach_vm_address_t>(
+ dyld_image_infos->dyldImageLoadAddress),
+ modules[index].reader->Address());
+ }
+}
+
+class ProcessReaderModulesChild final : public MachMultiprocess {
+ public:
+ ProcessReaderModulesChild() : MachMultiprocess() {}
+
+ ~ProcessReaderModulesChild() {}
+
+ private:
+ void MachMultiprocessParent() override {
+ ProcessReader process_reader;
+ ASSERT_TRUE(process_reader.Initialize(ChildTask()));
+
+ const std::vector<ProcessReader::Module>& modules =
+ process_reader.Modules();
+
+ // There needs to be at least an entry for the main executable, for a dylib,
+ // and for dyld.
+ ASSERT_GE(modules.size(), 3u);
+
+ FileHandle read_handle = ReadPipeHandle();
+
+ uint32_t expect_modules;
+ CheckedReadFile(read_handle, &expect_modules, sizeof(expect_modules));
+
+ ASSERT_EQ(expect_modules, modules.size());
+
+ bool found_cl_kernels = false;
+ for (size_t index = 0; index < modules.size(); ++index) {
+ SCOPED_TRACE(base::StringPrintf(
+ "index %zu, name %s", index, modules[index].name.c_str()));
+
+ uint32_t expect_name_length;
+ CheckedReadFile(
+ read_handle, &expect_name_length, sizeof(expect_name_length));
+
+ // The NUL terminator is not read.
+ std::string expect_name(expect_name_length, '\0');
+ CheckedReadFile(read_handle, &expect_name[0], expect_name_length);
+ EXPECT_EQ(expect_name, modules[index].name);
+
+ mach_vm_address_t expect_address;
+ CheckedReadFile(read_handle, &expect_address, sizeof(expect_address));
+ ASSERT_TRUE(modules[index].reader);
+ EXPECT_EQ(expect_address, modules[index].reader->Address());
+
+ if (index == 0 || index == modules.size() - 1) {
+ // dyld didn’t load the main executable or itself, so it couldn’t record
+ // these timestamps, and they are reported as 0.
+ EXPECT_EQ(0, modules[index].timestamp);
+ } else if (modules[index].reader->FileType() == MH_BUNDLE &&
+ modules[index].name == "cl_kernels") {
+ // cl_kernels doesn’t exist as a file.
+ EXPECT_EQ(0, modules[index].timestamp);
+ found_cl_kernels = true;
+ } else {
+ // Hope that the module didn’t change on disk.
+ struct stat stat_buf;
+ int rv = stat(expect_name.c_str(), &stat_buf);
+ EXPECT_EQ(0, rv) << ErrnoMessage("stat");
+ if (rv == 0) {
+ EXPECT_EQ(stat_buf.st_mtime, modules[index].timestamp);
+ }
+ }
+ }
+
+ EXPECT_EQ(ExpectCLKernels(), found_cl_kernels);
+ }
+
+ void MachMultiprocessChild() override {
+ FileHandle write_handle = WritePipeHandle();
+
+ uint32_t dyld_image_count = _dyld_image_count();
+ const struct dyld_all_image_infos* dyld_image_infos =
+ _dyld_get_all_image_infos();
+
+ uint32_t write_image_count = dyld_image_count;
+ if (dyld_image_infos->version >= 2) {
+ // dyld_image_count doesn’t include an entry for dyld itself, but one will
+ // be written.
+ ++write_image_count;
+ }
+
+ CheckedWriteFile(
+ write_handle, &write_image_count, sizeof(write_image_count));
+
+ for (size_t index = 0; index < write_image_count; ++index) {
+ const char* dyld_image_name;
+ mach_vm_address_t dyld_image_address;
+
+ if (index < dyld_image_count) {
+ dyld_image_name = _dyld_get_image_name(index);
+ dyld_image_address =
+ reinterpret_cast<mach_vm_address_t>(_dyld_get_image_header(index));
+ } else {
+ dyld_image_name = "/usr/lib/dyld";
+ dyld_image_address = reinterpret_cast<mach_vm_address_t>(
+ dyld_image_infos->dyldImageLoadAddress);
+ }
+
+ uint32_t dyld_image_name_length = strlen(dyld_image_name);
+ CheckedWriteFile(write_handle,
+ &dyld_image_name_length,
+ sizeof(dyld_image_name_length));
+
+ // The NUL terminator is not written.
+ CheckedWriteFile(write_handle, dyld_image_name, dyld_image_name_length);
+
+ CheckedWriteFile(
+ write_handle, &dyld_image_address, sizeof(dyld_image_address));
+ }
+
+ // Wait for the parent to signal that it’s OK to exit by closing its end of
+ // the pipe.
+ CheckedReadFileAtEOF(ReadPipeHandle());
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessReaderModulesChild);
+};
+
+TEST(ProcessReader, ChildModules) {
+ ScopedOpenCLNoOpKernel ensure_cl_kernels;
+ ASSERT_NO_FATAL_FAILURE(ensure_cl_kernels.SetUp());
+
+ ProcessReaderModulesChild process_reader_modules_child;
+ process_reader_modules_child.Run();
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/process_snapshot_mac.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_snapshot_mac.cc
new file mode 100644
index 00000000000..b8c0b531aa6
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_snapshot_mac.cc
@@ -0,0 +1,212 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/mac/process_snapshot_mac.h"
+
+#include "util/misc/tri_state.h"
+
+namespace crashpad {
+
+ProcessSnapshotMac::ProcessSnapshotMac()
+ : ProcessSnapshot(),
+ system_(),
+ threads_(),
+ modules_(),
+ exception_(),
+ process_reader_(),
+ report_id_(),
+ client_id_(),
+ annotations_simple_map_(),
+ snapshot_time_(),
+ initialized_() {
+}
+
+ProcessSnapshotMac::~ProcessSnapshotMac() {
+}
+
+bool ProcessSnapshotMac::Initialize(task_t task) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ if (gettimeofday(&snapshot_time_, nullptr) != 0) {
+ PLOG(ERROR) << "gettimeofday";
+ return false;
+ }
+
+ if (!process_reader_.Initialize(task)) {
+ return false;
+ }
+
+ system_.Initialize(&process_reader_, &snapshot_time_);
+
+ InitializeThreads();
+ InitializeModules();
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+bool ProcessSnapshotMac::InitializeException(
+ exception_behavior_t behavior,
+ thread_t exception_thread,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t flavor,
+ ConstThreadState state,
+ mach_msg_type_number_t state_count) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ DCHECK(!exception_);
+
+ exception_.reset(new internal::ExceptionSnapshotMac());
+ if (!exception_->Initialize(&process_reader_,
+ behavior,
+ exception_thread,
+ exception,
+ code,
+ code_count,
+ flavor,
+ state,
+ state_count)) {
+ exception_.reset();
+ return false;
+ }
+
+ return true;
+}
+
+void ProcessSnapshotMac::GetCrashpadOptions(
+ CrashpadInfoClientOptions* options) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ CrashpadInfoClientOptions local_options;
+
+ for (internal::ModuleSnapshotMac* module : modules_) {
+ CrashpadInfoClientOptions module_options;
+ module->GetCrashpadOptions(&module_options);
+
+ if (local_options.crashpad_handler_behavior == TriState::kUnset) {
+ local_options.crashpad_handler_behavior =
+ module_options.crashpad_handler_behavior;
+ }
+ if (local_options.system_crash_reporter_forwarding == TriState::kUnset) {
+ local_options.system_crash_reporter_forwarding =
+ module_options.system_crash_reporter_forwarding;
+ }
+
+ // If non-default values have been found for all options, the loop can end
+ // early.
+ if (local_options.crashpad_handler_behavior != TriState::kUnset &&
+ local_options.system_crash_reporter_forwarding != TriState::kUnset) {
+ break;
+ }
+ }
+
+ *options = local_options;
+}
+
+pid_t ProcessSnapshotMac::ProcessID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return process_reader_.ProcessID();
+}
+
+pid_t ProcessSnapshotMac::ParentProcessID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return process_reader_.ParentProcessID();
+}
+
+void ProcessSnapshotMac::SnapshotTime(timeval* snapshot_time) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *snapshot_time = snapshot_time_;
+}
+
+void ProcessSnapshotMac::ProcessStartTime(timeval* start_time) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ process_reader_.StartTime(start_time);
+}
+
+void ProcessSnapshotMac::ProcessCPUTimes(timeval* user_time,
+ timeval* system_time) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ process_reader_.CPUTimes(user_time, system_time);
+}
+
+void ProcessSnapshotMac::ReportID(UUID* report_id) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *report_id = report_id_;
+}
+
+void ProcessSnapshotMac::ClientID(UUID* client_id) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *client_id = client_id_;
+}
+
+const std::map<std::string, std::string>&
+ProcessSnapshotMac::AnnotationsSimpleMap() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return annotations_simple_map_;
+}
+
+const SystemSnapshot* ProcessSnapshotMac::System() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return &system_;
+}
+
+std::vector<const ThreadSnapshot*> ProcessSnapshotMac::Threads() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ std::vector<const ThreadSnapshot*> threads;
+ for (internal::ThreadSnapshotMac* thread : threads_) {
+ threads.push_back(thread);
+ }
+ return threads;
+}
+
+std::vector<const ModuleSnapshot*> ProcessSnapshotMac::Modules() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ std::vector<const ModuleSnapshot*> modules;
+ for (internal::ModuleSnapshotMac* module : modules_) {
+ modules.push_back(module);
+ }
+ return modules;
+}
+
+const ExceptionSnapshot* ProcessSnapshotMac::Exception() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return exception_.get();
+}
+
+void ProcessSnapshotMac::InitializeThreads() {
+ const std::vector<ProcessReader::Thread>& process_reader_threads =
+ process_reader_.Threads();
+ for (const ProcessReader::Thread& process_reader_thread :
+ process_reader_threads) {
+ auto thread = make_scoped_ptr(new internal::ThreadSnapshotMac());
+ if (thread->Initialize(&process_reader_, process_reader_thread)) {
+ threads_.push_back(thread.release());
+ }
+ }
+}
+
+void ProcessSnapshotMac::InitializeModules() {
+ const std::vector<ProcessReader::Module>& process_reader_modules =
+ process_reader_.Modules();
+ for (const ProcessReader::Module& process_reader_module :
+ process_reader_modules) {
+ auto module = make_scoped_ptr(new internal::ModuleSnapshotMac());
+ if (module->Initialize(&process_reader_, process_reader_module)) {
+ modules_.push_back(module.release());
+ }
+ }
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/process_snapshot_mac.h b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_snapshot_mac.h
new file mode 100644
index 00000000000..c74473852df
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_snapshot_mac.h
@@ -0,0 +1,153 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MAC_PROCESS_SNAPSHOT_MAC_H_
+#define CRASHPAD_SNAPSHOT_MAC_PROCESS_SNAPSHOT_MAC_H_
+
+#include <mach/mach.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "client/crashpad_info.h"
+#include "snapshot/crashpad_info_client_options.h"
+#include "snapshot/exception_snapshot.h"
+#include "snapshot/mac/exception_snapshot_mac.h"
+#include "snapshot/mac/module_snapshot_mac.h"
+#include "snapshot/mac/process_reader.h"
+#include "snapshot/mac/system_snapshot_mac.h"
+#include "snapshot/mac/thread_snapshot_mac.h"
+#include "snapshot/module_snapshot.h"
+#include "snapshot/process_snapshot.h"
+#include "snapshot/system_snapshot.h"
+#include "snapshot/thread_snapshot.h"
+#include "util/mach/mach_extensions.h"
+#include "util/misc/initialization_state_dcheck.h"
+#include "util/misc/uuid.h"
+#include "util/stdlib/pointer_container.h"
+
+namespace crashpad {
+
+//! \brief A ProcessSnapshot of a running (or crashed) process running on a Mac
+//! OS X system.
+class ProcessSnapshotMac final : public ProcessSnapshot {
+ public:
+ ProcessSnapshotMac();
+ ~ProcessSnapshotMac() override;
+
+ //! \brief Initializes the object.
+ //!
+ //! \param[in] task The task to create a snapshot from.
+ //!
+ //! \return `true` if the snapshot could be created, `false` otherwise with
+ //! an appropriate message logged.
+ bool Initialize(task_t task);
+
+ //! \brief Initializes the object’s exception.
+ //!
+ //! This populates the data to be returned by Exception(). The parameters may
+ //! be passed directly through from a Mach exception handler.
+ //!
+ //! This method must not be called until after a successful call to
+ //! Initialize().
+ //!
+ //! \return `true` if the exception information could be initialized, `false`
+ //! otherwise with an appropriate message logged. When this method returns
+ //! `false`, the ProcessSnapshotMac object’s validity remains unchanged.
+ bool InitializeException(exception_behavior_t behavior,
+ thread_t exception_thread,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t flavor,
+ ConstThreadState state,
+ mach_msg_type_number_t state_count);
+
+ //! \brief Sets the value to be returned by ReportID().
+ //!
+ //! On Mac OS X, the crash report ID is under the control of the snapshot
+ //! producer, which may call this method to set the report ID. If this is not
+ //! done, ReportID() will return an identifier consisting entirely of zeroes.
+ void SetReportID(const UUID& report_id) { report_id_ = report_id; }
+
+ //! \brief Sets the value to be returned by ClientID().
+ //!
+ //! On Mac OS X, the client ID is under the control of the snapshot producer,
+ //! which may call this method to set the client ID. If this is not done,
+ //! ClientID() will return an identifier consisting entirely of zeroes.
+ void SetClientID(const UUID& client_id) { client_id_ = client_id; }
+
+ //! \brief Sets the value to be returned by AnnotationsSimpleMap().
+ //!
+ //! On Mac OS X, all process annotations are under the control of the snapshot
+ //! producer, which may call this method to establish these annotations.
+ //! Contrast this with module annotations, which are under the control of the
+ //! process being snapshotted.
+ void SetAnnotationsSimpleMap(
+ const std::map<std::string, std::string>& annotations_simple_map) {
+ annotations_simple_map_ = annotations_simple_map;
+ }
+
+ //! \brief Returns options from CrashpadInfo structures found in modules in
+ //! the process.
+ //!
+ //! \param[out] options Options set in CrashpadInfo structures in modules in
+ //! the process.
+ void GetCrashpadOptions(CrashpadInfoClientOptions* options);
+
+ // ProcessSnapshot:
+
+ pid_t ProcessID() const override;
+ pid_t ParentProcessID() const override;
+ void SnapshotTime(timeval* snapshot_time) const override;
+ void ProcessStartTime(timeval* start_time) const override;
+ void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override;
+ void ReportID(UUID* report_id) const override;
+ void ClientID(UUID* client_id) const override;
+ const std::map<std::string, std::string>& AnnotationsSimpleMap()
+ const override;
+ const SystemSnapshot* System() const override;
+ std::vector<const ThreadSnapshot*> Threads() const override;
+ std::vector<const ModuleSnapshot*> Modules() const override;
+ const ExceptionSnapshot* Exception() const override;
+
+ private:
+ // Initializes threads_ on behalf of Initialize().
+ void InitializeThreads();
+
+ // Initializes modules_ on behalf of Initialize().
+ void InitializeModules();
+
+ internal::SystemSnapshotMac system_;
+ PointerVector<internal::ThreadSnapshotMac> threads_;
+ PointerVector<internal::ModuleSnapshotMac> modules_;
+ scoped_ptr<internal::ExceptionSnapshotMac> exception_;
+ ProcessReader process_reader_;
+ UUID report_id_;
+ UUID client_id_;
+ std::map<std::string, std::string> annotations_simple_map_;
+ timeval snapshot_time_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotMac);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MAC_PROCESS_SNAPSHOT_MAC_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types.cc
new file mode 100644
index 00000000000..8c907b2f595
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types.cc
@@ -0,0 +1,244 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/mac/process_types.h"
+
+#include <string.h>
+#include <uuid/uuid.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "snapshot/mac/process_types/internal.h"
+#include "util/mach/task_memory.h"
+
+namespace crashpad {
+namespace {
+
+// Assign() is used by each flavor's ReadInto implementation to copy data from a
+// specific struct to a generic struct. For fundamental types, the assignment
+// operator suffices. For other types such as arrays, an explicit Assign
+// specialization is needed, typically performing a copy.
+
+template <typename DestinationType, typename SourceType>
+inline void Assign(DestinationType* destination, const SourceType& source) {
+ *destination = source;
+}
+
+template <>
+inline void Assign<process_types::internal::Reserved64Only64,
+ process_types::internal::Reserved64Only32>(
+ process_types::internal::Reserved64Only64* destination,
+ const process_types::internal::Reserved64Only32& source) {
+ // Reserved64Only32 carries no data.
+ *destination = 0;
+}
+
+using CharArray16 = char[16];
+template <>
+inline void Assign<CharArray16, CharArray16>(CharArray16* destination,
+ const CharArray16& source) {
+ memcpy(destination, &source, sizeof(source));
+}
+
+using UInt64Array16 = uint64_t[16];
+template <>
+inline void Assign<UInt64Array16, UInt64Array16>(UInt64Array16* destination,
+ const UInt64Array16& source) {
+ memcpy(destination, &source, sizeof(source));
+}
+
+using UInt32Array16 = uint32_t[16];
+template <>
+inline void Assign<UInt64Array16, UInt32Array16>(UInt64Array16* destination,
+ const UInt32Array16& source) {
+ for (size_t index = 0; index < arraysize(source); ++index) {
+ (*destination)[index] = source[index];
+ }
+}
+
+template <>
+inline void Assign<uuid_t, uuid_t>(uuid_t* destination, const uuid_t& source) {
+ // uuid_t is a type alias for unsigned char[16].
+ memcpy(destination, &source, sizeof(source));
+}
+
+} // namespace
+} // namespace crashpad
+
+// Implement the generic crashpad::process_types::struct_name ReadInto(), which
+// delegates to the templatized ReadIntoInternal(), which reads the specific
+// struct type from the remote process and genericizes it. Also implement
+// crashpad::process_types::internal::struct_name<> GenericizeInto(), which
+// operates on each member in the struct.
+#define PROCESS_TYPE_STRUCT_IMPLEMENT 1
+
+#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) \
+ namespace crashpad { \
+ namespace process_types { \
+ \
+ size_t struct_name::ExpectedSize(ProcessReader* process_reader) { \
+ if (!process_reader->Is64Bit()) { \
+ return internal::struct_name<internal::Traits32>::Size(); \
+ } else { \
+ return internal::struct_name<internal::Traits64>::Size(); \
+ } \
+ } \
+ \
+ bool struct_name::ReadInto(ProcessReader* process_reader, \
+ mach_vm_address_t address, \
+ struct_name* generic) { \
+ if (!process_reader->Is64Bit()) { \
+ return ReadIntoInternal<internal::struct_name<internal::Traits32> >( \
+ process_reader, address, generic); \
+ } else { \
+ return ReadIntoInternal<internal::struct_name<internal::Traits64> >( \
+ process_reader, address, generic); \
+ } \
+ } \
+ \
+ template <typename T> \
+ bool struct_name::ReadIntoInternal(ProcessReader* process_reader, \
+ mach_vm_address_t address, \
+ struct_name* generic) { \
+ T specific; \
+ if (!specific.Read(process_reader, address)) { \
+ return false; \
+ } \
+ specific.GenericizeInto(generic, &generic->size_); \
+ return true; \
+ } \
+ \
+ namespace internal { \
+ \
+ template <typename Traits> \
+ void struct_name<Traits>::GenericizeInto( \
+ process_types::struct_name* generic, \
+ size_t* specific_size) { \
+ *specific_size = Size();
+
+#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) \
+ Assign(&generic->member_name, member_name);
+
+#define PROCESS_TYPE_STRUCT_END(struct_name) \
+ } \
+ } /* namespace internal */ \
+ } /* namespace process_types */ \
+ } /* namespace crashpad */
+
+#include "snapshot/mac/process_types/all.proctype"
+
+#undef PROCESS_TYPE_STRUCT_BEGIN
+#undef PROCESS_TYPE_STRUCT_MEMBER
+#undef PROCESS_TYPE_STRUCT_END
+#undef PROCESS_TYPE_STRUCT_IMPLEMENT
+
+// Implement the specific crashpad::process_types::internal::struct_name<>
+// ReadInto(). The default implementation simply reads the struct from the
+// remote process. This is separated from other method implementations because
+// some types may wish to provide custom readers. This can be done by guarding
+// such types’ proctype definitions against this macro and providing custom
+// implementations in snapshot/mac/process_types/custom.cc.
+#define PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO 1
+
+#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) \
+ namespace crashpad { \
+ namespace process_types { \
+ namespace internal { \
+ \
+ template <typename Traits> \
+ bool struct_name<Traits>::ReadInto(ProcessReader* process_reader, \
+ mach_vm_address_t address, \
+ struct_name<Traits>* specific) { \
+ return process_reader->Memory()->Read( \
+ address, sizeof(*specific), specific); \
+ } \
+ } /* namespace internal */ \
+ } /* namespace process_types */ \
+ } /* namespace crashpad */
+
+#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...)
+
+#define PROCESS_TYPE_STRUCT_END(struct_name)
+
+#include "snapshot/mac/process_types/all.proctype"
+
+#undef PROCESS_TYPE_STRUCT_BEGIN
+#undef PROCESS_TYPE_STRUCT_MEMBER
+#undef PROCESS_TYPE_STRUCT_END
+#undef PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO
+
+// Implement the array operations. These are separated from other method
+// implementations because some types are variable-length and are never stored
+// as direct-access arrays. It would be incorrect to provide reader
+// implementations for such types. Types that wish to suppress array operations
+// can do so by guarding their proctype definitions against this macro.
+#define PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY 1
+
+#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) \
+ namespace crashpad { \
+ namespace process_types { \
+ namespace internal { \
+ \
+ template <typename Traits> \
+ bool struct_name<Traits>::ReadArrayInto(ProcessReader* process_reader, \
+ mach_vm_address_t address, \
+ size_t count, \
+ struct_name<Traits>* specific) { \
+ return process_reader->Memory()->Read( \
+ address, sizeof(struct_name<Traits> [count]), specific); \
+ } \
+ } /* namespace internal */ \
+ \
+ bool struct_name::ReadArrayInto(ProcessReader* process_reader, \
+ mach_vm_address_t address, \
+ size_t count, \
+ struct_name* generic) { \
+ if (!process_reader->Is64Bit()) { \
+ return ReadArrayIntoInternal< \
+ internal::struct_name<internal::Traits32> >( \
+ process_reader, address, count, generic); \
+ } else { \
+ return ReadArrayIntoInternal< \
+ internal::struct_name<internal::Traits64> >( \
+ process_reader, address, count, generic); \
+ } \
+ return true; \
+ } \
+ \
+ template <typename T> \
+ bool struct_name::ReadArrayIntoInternal(ProcessReader* process_reader, \
+ mach_vm_address_t address, \
+ size_t count, \
+ struct_name* generic) { \
+ scoped_ptr<T[]> specific(new T[count]); \
+ if (!T::ReadArrayInto(process_reader, address, count, &specific[0])) { \
+ return false; \
+ } \
+ for (size_t index = 0; index < count; ++index) { \
+ specific[index].GenericizeInto(&generic[index], &generic[index].size_); \
+ } \
+ return true; \
+ } \
+ } /* namespace process_types */ \
+ } /* namespace crashpad */
+
+#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...)
+
+#define PROCESS_TYPE_STRUCT_END(struct_name)
+
+#include "snapshot/mac/process_types/all.proctype"
+
+#undef PROCESS_TYPE_STRUCT_BEGIN
+#undef PROCESS_TYPE_STRUCT_MEMBER
+#undef PROCESS_TYPE_STRUCT_END
+#undef PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types.h b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types.h
new file mode 100644
index 00000000000..b38fea789c4
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types.h
@@ -0,0 +1,183 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_H_
+#define CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_H_
+
+#include <mach/mach.h>
+#include <mach-o/dyld_images.h>
+#include <mach-o/loader.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "snapshot/mac/process_reader.h"
+
+namespace crashpad {
+namespace process_types {
+namespace internal {
+
+// Some structure definitions differ in 32-bit and 64-bit environments by having
+// additional “reserved” padding fields present only in the 64-bit environment.
+// These Reserved64Only* types allow the process_types system to replicate these
+// structures more precisely.
+using Reserved64Only32 = char[0];
+using Reserved64Only64 = uint32_t;
+
+} // namespace internal
+} // namespace process_types
+} // namespace crashpad
+
+#include "snapshot/mac/process_types/traits.h"
+
+// Creates the traits type crashpad::process_types::internal::TraitsGeneric.
+DECLARE_PROCESS_TYPE_TRAITS_CLASS(Generic, 64)
+
+#undef DECLARE_PROCESS_TYPE_TRAITS_CLASS
+
+// Declare the crashpad::process_types::struct_name structs. These are the
+// user-visible generic structs that callers will interact with. They read data
+// from 32-bit or 64-bit processes by using the specific internal templatized
+// structs below.
+#define PROCESS_TYPE_STRUCT_DECLARE 1
+
+#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) \
+ namespace crashpad { \
+ namespace process_types { \
+ struct struct_name { \
+ public: \
+ using Long = internal::TraitsGeneric::Long; \
+ using ULong = internal::TraitsGeneric::ULong; \
+ using Pointer = internal::TraitsGeneric::Pointer; \
+ using IntPtr = internal::TraitsGeneric::IntPtr; \
+ using UIntPtr = internal::TraitsGeneric::UIntPtr; \
+ using Reserved64Only = internal::TraitsGeneric::Reserved64Only; \
+ \
+ /* Initializes an object with data read from |process_reader| at \
+ * |address|, properly genericized. */ \
+ bool Read(ProcessReader* process_reader, mach_vm_address_t address) { \
+ return ReadInto(process_reader, address, this); \
+ } \
+ \
+ /* Reads |count| objects from |process_reader| beginning at |address|, and \
+ * genericizes the objects. The caller must provide storage for |count| \
+ * objects in |generic|. */ \
+ static bool ReadArrayInto(ProcessReader* process_reader, \
+ mach_vm_address_t address, \
+ size_t count, \
+ struct_name* generic); \
+ \
+ /* Returns the size of the object that was read. This is the size of the \
+ * storage in the process that the data is read from, and is not the same \
+ * as the size of the generic struct. */ \
+ size_t Size() const { return size_; } \
+ \
+ /* Similar to Size(), but computes the expected size of a structure based \
+ * on the process’ bitness. This can be used prior to reading any data \
+ * from a process. */ \
+ static size_t ExpectedSize(ProcessReader* process_reader);
+
+#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) \
+ member_type member_name __VA_ARGS__;
+
+#define PROCESS_TYPE_STRUCT_END(struct_name) \
+ private: \
+ /* The static form of Read(). Populates the struct at |generic|. */ \
+ static bool ReadInto(ProcessReader* process_reader, \
+ mach_vm_address_t address, \
+ struct_name* generic); \
+ \
+ template <typename T> \
+ static bool ReadIntoInternal(ProcessReader* process_reader, \
+ mach_vm_address_t address, \
+ struct_name* generic); \
+ template <typename T> \
+ static bool ReadArrayIntoInternal(ProcessReader* process_reader, \
+ mach_vm_address_t address, \
+ size_t count, \
+ struct_name* generic); \
+ size_t size_; \
+ }; \
+ } /* namespace process_types */ \
+ } /* namespace crashpad */
+
+#include "snapshot/mac/process_types/all.proctype"
+
+#undef PROCESS_TYPE_STRUCT_BEGIN
+#undef PROCESS_TYPE_STRUCT_MEMBER
+#undef PROCESS_TYPE_STRUCT_END
+#undef PROCESS_TYPE_STRUCT_DECLARE
+
+// Declare the templatized crashpad::process_types::internal::struct_name<>
+// structs. These are the 32-bit and 64-bit specific structs that describe the
+// layout of objects in another process. This is repeated instead of being
+// shared with the generic declaration above because both the generic and
+// templatized specific structs need all of the struct members declared.
+//
+//
+// GenericizeInto() translates a struct from the representation used in the
+// remote process into the generic form.
+#define PROCESS_TYPE_STRUCT_DECLARE_INTERNAL 1
+
+#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) \
+ namespace crashpad { \
+ namespace process_types { \
+ namespace internal { \
+ template <typename Traits> \
+ struct struct_name { \
+ public: \
+ using Long = typename Traits::Long; \
+ using ULong = typename Traits::ULong; \
+ using Pointer = typename Traits::Pointer; \
+ using IntPtr = typename Traits::IntPtr; \
+ using UIntPtr = typename Traits::UIntPtr; \
+ using Reserved64Only = typename Traits::Reserved64Only; \
+ \
+ /* Read(), ReadArrayInto(), and Size() are as in the generic user-visible \
+ * struct above. */ \
+ bool Read(ProcessReader* process_reader, mach_vm_address_t address) { \
+ return ReadInto(process_reader, address, this); \
+ } \
+ static bool ReadArrayInto(ProcessReader* process_reader, \
+ mach_vm_address_t address, \
+ size_t count, \
+ struct_name<Traits>* specific); \
+ static size_t Size() { return sizeof(struct_name<Traits>); } \
+ \
+ /* Translates a struct from the representation used in the remote process \
+ * into the generic form. */ \
+ void GenericizeInto(process_types::struct_name* generic, \
+ size_t* specific_size);
+
+#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) \
+ member_type member_name __VA_ARGS__;
+
+#define PROCESS_TYPE_STRUCT_END(struct_name) \
+ private: \
+ /* ReadInto() is as in the generic user-visible struct above. */ \
+ static bool ReadInto(ProcessReader* process_reader, \
+ mach_vm_address_t address, \
+ struct_name<Traits>* specific); \
+ }; \
+ } /* namespace internal */ \
+ } /* namespace process_types */ \
+ } /* namespace crashpad */
+
+#include "snapshot/mac/process_types/all.proctype"
+
+#undef PROCESS_TYPE_STRUCT_BEGIN
+#undef PROCESS_TYPE_STRUCT_MEMBER
+#undef PROCESS_TYPE_STRUCT_END
+#undef PROCESS_TYPE_STRUCT_DECLARE_INTERNAL
+
+#endif // CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/all.proctype b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/all.proctype
new file mode 100644
index 00000000000..7ef54d5c6ce
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/all.proctype
@@ -0,0 +1,26 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This file is intended to be included multiple times in the same translation
+// unit, so #include guards are intentionally absent.
+//
+// This file is included by snapshot/mac/process_types.h and
+// snapshot/mac/process_types.cc to produce process type struct definitions and
+// accessors.
+
+#include "snapshot/mac/process_types/crashpad_info.proctype"
+#include "snapshot/mac/process_types/crashreporterclient.proctype"
+#include "snapshot/mac/process_types/dyld_images.proctype"
+#include "snapshot/mac/process_types/loader.proctype"
+#include "snapshot/mac/process_types/nlist.proctype"
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/crashpad_info.proctype b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/crashpad_info.proctype
new file mode 100644
index 00000000000..cdc8247fc66
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/crashpad_info.proctype
@@ -0,0 +1,39 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// The file corresponds to Crashpad’s client/crashpad_info.h.
+//
+// This file is intended to be included multiple times in the same translation
+// unit, so #include guards are intentionally absent.
+//
+// This file is included by snapshot/mac/process_types.h and
+// snapshot/mac/process_types.cc to produce process type struct definitions and
+// accessors.
+
+// Client Mach-O images will contain a __DATA,__crashpad_info section formatted
+// according to this structure.
+PROCESS_TYPE_STRUCT_BEGIN(CrashpadInfo)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, signature)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, size)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, version)
+ PROCESS_TYPE_STRUCT_MEMBER(uint8_t, crashpad_handler_behavior) // TriState
+
+ // TriState
+ PROCESS_TYPE_STRUCT_MEMBER(uint8_t, system_crash_reporter_forwarding)
+
+ PROCESS_TYPE_STRUCT_MEMBER(uint16_t, padding_0)
+
+ // SimpleStringDictionary*
+ PROCESS_TYPE_STRUCT_MEMBER(Pointer, simple_annotations)
+PROCESS_TYPE_STRUCT_END(CrashpadInfo)
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/crashreporterclient.proctype b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/crashreporterclient.proctype
new file mode 100644
index 00000000000..7d83c2334b0
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/crashreporterclient.proctype
@@ -0,0 +1,40 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// The name of this file was chosen based on
+// http://llvm.org/svn/llvm-project/llvm/trunk/lib/Support/PrettyStackTrace.cpp.
+// The name of the structure it describes was chosen based on that file as well
+// as 10.9.2 cups-372.2/cups/backend/usb-darwin.c. That file also provided the
+// names and types of the fields in the structure.
+//
+// This file is intended to be included multiple times in the same translation
+// unit, so #include guards are intentionally absent.
+//
+// This file is included by snapshot/mac/process_types.h and
+// snapshot/mac/process_types.cc to produce process type struct definitions and
+// accessors.
+
+// Client Mach-O images will contain a __DATA,__crash_info section formatted
+// according to this structure.
+PROCESS_TYPE_STRUCT_BEGIN(crashreporter_annotations_t)
+ // The only known version is 4.
+ PROCESS_TYPE_STRUCT_MEMBER(uint64_t, version) // unsigned long
+
+ PROCESS_TYPE_STRUCT_MEMBER(uint64_t, message) // char*
+ PROCESS_TYPE_STRUCT_MEMBER(uint64_t, signature_string) // char*
+ PROCESS_TYPE_STRUCT_MEMBER(uint64_t, backtrace) // char*
+ PROCESS_TYPE_STRUCT_MEMBER(uint64_t, message2) // char*
+ PROCESS_TYPE_STRUCT_MEMBER(uint64_t, thread)
+ PROCESS_TYPE_STRUCT_MEMBER(uint64_t, dialog_mode) // unsigned int
+PROCESS_TYPE_STRUCT_END(crashreporter_annotations_t)
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/custom.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/custom.cc
new file mode 100644
index 00000000000..49553cf95f1
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/custom.cc
@@ -0,0 +1,95 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/mac/process_types.h"
+
+#include <string.h>
+
+#include "snapshot/mac/process_types/internal.h"
+#include "util/mach/task_memory.h"
+
+namespace crashpad {
+namespace process_types {
+namespace internal {
+
+template <typename Traits>
+bool dyld_all_image_infos<Traits>::ReadInto(
+ ProcessReader* process_reader,
+ mach_vm_address_t address,
+ dyld_all_image_infos<Traits>* specific) {
+ TaskMemory* task_memory = process_reader->Memory();
+ if (!task_memory->Read(
+ address, sizeof(specific->version), &specific->version)) {
+ return false;
+ }
+
+ mach_vm_size_t size;
+ if (specific->version >= 14) {
+ size = sizeof(dyld_all_image_infos<Traits>);
+ } else if (specific->version >= 13) {
+ size = offsetof(dyld_all_image_infos<Traits>, reserved);
+ } else if (specific->version >= 12) {
+ size = offsetof(dyld_all_image_infos<Traits>, sharedCacheUUID);
+ } else if (specific->version >= 11) {
+ size = offsetof(dyld_all_image_infos<Traits>, sharedCacheSlide);
+ } else if (specific->version >= 10) {
+ size = offsetof(dyld_all_image_infos<Traits>, errorKind);
+ } else if (specific->version >= 9) {
+ size = offsetof(dyld_all_image_infos<Traits>, initialImageCount);
+ } else if (specific->version >= 8) {
+ size = offsetof(dyld_all_image_infos<Traits>, dyldAllImageInfosAddress);
+ } else if (specific->version >= 7) {
+ size = offsetof(dyld_all_image_infos<Traits>, uuidArrayCount);
+ } else if (specific->version >= 6) {
+ size = offsetof(dyld_all_image_infos<Traits>, systemOrderFlag);
+ } else if (specific->version >= 5) {
+ size = offsetof(dyld_all_image_infos<Traits>, coreSymbolicationShmPage);
+ } else if (specific->version >= 3) {
+ size = offsetof(dyld_all_image_infos<Traits>, dyldVersion);
+ } else if (specific->version >= 2) {
+ size = offsetof(dyld_all_image_infos<Traits>, jitInfo);
+ } else if (specific->version >= 1) {
+ size = offsetof(dyld_all_image_infos<Traits>, libSystemInitialized);
+ } else {
+ size = offsetof(dyld_all_image_infos<Traits>, infoArrayCount);
+ }
+
+ if (!task_memory->Read(address, size, specific)) {
+ return false;
+ }
+
+ // Zero out the rest of the structure in case anything accesses fields without
+ // checking the version.
+ size_t remaining = sizeof(*specific) - size;
+ if (remaining > 0) {
+ char* start = reinterpret_cast<char*>(specific) + size;
+ memset(start, 0, remaining);
+ }
+
+ return true;
+}
+
+#define PROCESS_TYPE_FLAVOR_TRAITS(lp_bits) \
+ template bool dyld_all_image_infos<Traits##lp_bits>::ReadInto( \
+ ProcessReader*, \
+ mach_vm_address_t, \
+ dyld_all_image_infos<Traits##lp_bits>*);
+
+#include "snapshot/mac/process_types/flavors.h"
+
+#undef PROCESS_TYPE_FLAVOR_TRAITS
+
+} // namespace internal
+} // namespace process_types
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/dyld_images.proctype b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/dyld_images.proctype
new file mode 100644
index 00000000000..3e45c99213e
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/dyld_images.proctype
@@ -0,0 +1,110 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This file corresponds to the system’s <mach-o/dyld_images.h>.
+//
+// This file is intended to be included multiple times in the same translation
+// unit, so #include guards are intentionally absent.
+//
+// This file is included by snapshot/mac/process_types.h and
+// snapshot/mac/process_types.cc to produce process type struct definitions and
+// accessors.
+
+PROCESS_TYPE_STRUCT_BEGIN(dyld_image_info)
+ PROCESS_TYPE_STRUCT_MEMBER(Pointer, imageLoadAddress) // const mach_header*
+ PROCESS_TYPE_STRUCT_MEMBER(Pointer, imageFilePath) // const char*
+ PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, imageFileModDate)
+PROCESS_TYPE_STRUCT_END(dyld_image_info)
+
+PROCESS_TYPE_STRUCT_BEGIN(dyld_uuid_info)
+ PROCESS_TYPE_STRUCT_MEMBER(Pointer, imageLoadAddress) // const mach_header*
+ PROCESS_TYPE_STRUCT_MEMBER(uuid_t, imageUUID)
+PROCESS_TYPE_STRUCT_END(dyld_uuid_info)
+
+// dyld_all_image_infos is variable-length. Its length dictated by its |version|
+// field which is always present. A custom implementation of the flavored
+// ReadSpecificInto function that understands how to map this field to the
+// structure’s actual size is provided in snapshot/mac/process_types/custom.cc.
+// No implementation of ReadArrayInto is provided because dyld_all_image_infos
+// structs are singletons in a process and are never present in arrays, so the
+// functionality is unnecessary.
+
+#if !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO) && \
+ !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY)
+
+PROCESS_TYPE_STRUCT_BEGIN(dyld_all_image_infos)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, version)
+
+ // Version 1 (Mac OS X 10.4)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, infoArrayCount)
+ PROCESS_TYPE_STRUCT_MEMBER(Pointer, infoArray) // const dyld_image_info*
+ PROCESS_TYPE_STRUCT_MEMBER(Pointer, notification) // function pointer
+ PROCESS_TYPE_STRUCT_MEMBER(bool, processDetachedFromSharedRegion)
+
+ // Version 2 (Mac OS X 10.6)
+ PROCESS_TYPE_STRUCT_MEMBER(bool, libSystemInitialized)
+
+ // This field does not appear in the system’s structure definition but is
+ // necessary to ensure that when building in 32-bit mode, the 64-bit version
+ // of the process_types structure matches the genuine 64-bit structure. This
+ // is required because the alignment constraints on 64-bit types are more
+ // stringent in 64-bit mode.
+ PROCESS_TYPE_STRUCT_MEMBER(Reserved64Only, alignment)
+
+ // const mach_header*
+ PROCESS_TYPE_STRUCT_MEMBER(Pointer, dyldImageLoadAddress)
+
+ // Version 3 (Mac OS X 10.6)
+ PROCESS_TYPE_STRUCT_MEMBER(Pointer, jitInfo) // void*
+
+ // Version 5 (Mac OS X 10.6)
+ PROCESS_TYPE_STRUCT_MEMBER(Pointer, dyldVersion) // const char*
+ PROCESS_TYPE_STRUCT_MEMBER(Pointer, errorMessage) // const char*
+ PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, terminationFlags)
+
+ // Version 6 (Mac OS X 10.6)
+ PROCESS_TYPE_STRUCT_MEMBER(Pointer, coreSymbolicationShmPage) // void*
+
+ // Version 7 (Mac OS X 10.6)
+ PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, systemOrderFlag)
+
+ // Version 8 (Mac OS X 10.7)
+ PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, uuidArrayCount)
+ PROCESS_TYPE_STRUCT_MEMBER(Pointer, uuidArray) // const dyld_uuid_info*
+
+ // Version 9 (Mac OS X 10.7)
+ // dyld_all_image_infos*
+ PROCESS_TYPE_STRUCT_MEMBER(Pointer, dyldAllImageInfosAddress)
+
+ // Version 10 (Mac OS X 10.7)
+ PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, initialImageCount)
+
+ // Version 11 (Mac OS X 10.7)
+ PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, errorKind)
+ PROCESS_TYPE_STRUCT_MEMBER(Pointer, errorClientOfDylibPath) // const char*
+ PROCESS_TYPE_STRUCT_MEMBER(Pointer, errorTargetDylibPath) // const char*
+ PROCESS_TYPE_STRUCT_MEMBER(Pointer, errorSymbol) // const char*
+
+ // Version 12 (Mac OS X 10.7)
+ PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, sharedCacheSlide)
+
+ // Version 13 (Mac OS X 10.9)
+ PROCESS_TYPE_STRUCT_MEMBER(uint8_t, sharedCacheUUID, [16])
+
+ // Version 14 (Mac OS X 10.9)
+ PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, reserved, [16])
+PROCESS_TYPE_STRUCT_END(dyld_all_image_infos)
+
+#endif // ! PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO &&
+ // ! PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/flavors.h b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/flavors.h
new file mode 100644
index 00000000000..01ab2bd7cb4
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/flavors.h
@@ -0,0 +1,24 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This file is intended to be included multiple times in the same translation
+// unit, so #include guards are intentionally absent.
+//
+// This file is included by snapshot/mac/process_types/internal.h to produce
+// process type flavor traits class declarations and by
+// snapshot/mac/process_types/custom.cc to provide explicit instantiation of
+// flavored implementations.
+
+PROCESS_TYPE_FLAVOR_TRAITS(32)
+PROCESS_TYPE_FLAVOR_TRAITS(64)
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/internal.h b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/internal.h
new file mode 100644
index 00000000000..fd956574d2c
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/internal.h
@@ -0,0 +1,36 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_INTERNAL_H_
+#define CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_INTERNAL_H_
+
+#include "snapshot/mac/process_types.h"
+
+// Declare Traits32 and Traits64, flavor-specific traits classes. These are
+// private traits classes not for use outside of process type internals.
+// TraitsGeneric is declared in snapshot/mac/process_types.h.
+
+#include "snapshot/mac/process_types/traits.h"
+
+#define PROCESS_TYPE_FLAVOR_TRAITS(lp_bits) \
+ DECLARE_PROCESS_TYPE_TRAITS_CLASS( \
+ lp_bits, lp_bits, __attribute__((aligned(lp_bits / 8))))
+
+#include "snapshot/mac/process_types/flavors.h"
+
+#undef PROCESS_TYPE_FLAVOR_TRAITS
+
+#undef DECLARE_PROCESS_TYPE_TRAITS_CLASS
+
+#endif // CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_INTERNAL_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/loader.proctype b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/loader.proctype
new file mode 100644
index 00000000000..177e6f72fbb
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/loader.proctype
@@ -0,0 +1,140 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This file corresponds to the system’s <mach-o/loader.h>.
+//
+// This file is intended to be included multiple times in the same translation
+// unit, so #include guards are intentionally absent.
+//
+// This file is included by snapshot/mac/process_types.h and
+// snapshot/mac/process_types.cc to produce process type struct definitions and
+// accessors.
+
+PROCESS_TYPE_STRUCT_BEGIN(mach_header) // 64-bit: mach_header_64
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, magic)
+
+ // cputype is really cpu_type_t, a typedef for integer_t, itself a typedef for
+ // int. It is currently reasonable to assume that int is int32_t.
+ PROCESS_TYPE_STRUCT_MEMBER(int32_t, cputype)
+
+ // cpusubtype is really cpu_subtype_t, a typedef for integer_t, itself a
+ // typedef for int. It is currently reasonable to assume that int is int32_t.
+ PROCESS_TYPE_STRUCT_MEMBER(int32_t, cpusubtype)
+
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, filetype)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, ncmds)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, sizeofcmds)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, flags)
+ PROCESS_TYPE_STRUCT_MEMBER(Reserved64Only, reserved)
+PROCESS_TYPE_STRUCT_END(mach_header)
+
+PROCESS_TYPE_STRUCT_BEGIN(load_command)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize)
+PROCESS_TYPE_STRUCT_END(load_command)
+
+PROCESS_TYPE_STRUCT_BEGIN(segment_command) // 64-bit: segment_command_64
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize)
+
+ // This string is not necessarily NUL-terminated.
+ PROCESS_TYPE_STRUCT_MEMBER(char, segname, [16])
+
+ PROCESS_TYPE_STRUCT_MEMBER(ULong, vmaddr)
+ PROCESS_TYPE_STRUCT_MEMBER(ULong, vmsize)
+ PROCESS_TYPE_STRUCT_MEMBER(ULong, fileoff)
+ PROCESS_TYPE_STRUCT_MEMBER(ULong, filesize)
+ PROCESS_TYPE_STRUCT_MEMBER(vm_prot_t, maxprot)
+ PROCESS_TYPE_STRUCT_MEMBER(vm_prot_t, initprot)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nsects)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, flags)
+PROCESS_TYPE_STRUCT_END(segment_command)
+
+PROCESS_TYPE_STRUCT_BEGIN(dylib_command)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize)
+
+ // The following come from the dylib struct member of dylib_command.
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, dylib_name) // lc_str
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, dylib_timestamp)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, dylib_current_version)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, dylib_compatibility_version)
+PROCESS_TYPE_STRUCT_END(dylib_command)
+
+PROCESS_TYPE_STRUCT_BEGIN(dylinker_command)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, name) // lc_str
+PROCESS_TYPE_STRUCT_END(dylinker_command)
+
+PROCESS_TYPE_STRUCT_BEGIN(symtab_command)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, symoff)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nsyms)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, stroff)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, strsize)
+PROCESS_TYPE_STRUCT_END(symtab_command)
+
+PROCESS_TYPE_STRUCT_BEGIN(dysymtab_command)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, ilocalsym)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nlocalsym)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, iextdefsym)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nextdefsym)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, iundefsym)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nundefsym)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, tocoff)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, ntoc)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, modtaboff)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nmodtab)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, extrefsymoff)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nextrefsyms)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, indirectsymoff)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nindirectsyms)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, extreloff)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nextrel)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, locreloff)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nlocrel)
+PROCESS_TYPE_STRUCT_END(dysymtab_command)
+
+PROCESS_TYPE_STRUCT_BEGIN(uuid_command)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize)
+ PROCESS_TYPE_STRUCT_MEMBER(uint8_t, uuid, [16])
+PROCESS_TYPE_STRUCT_END(uuid_command)
+
+PROCESS_TYPE_STRUCT_BEGIN(source_version_command)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize)
+ PROCESS_TYPE_STRUCT_MEMBER(uint64_t, version)
+PROCESS_TYPE_STRUCT_END(source_version_command)
+
+PROCESS_TYPE_STRUCT_BEGIN(section) // 64-bit: section_64
+ // These strings are not necessarily NUL-terminated.
+ PROCESS_TYPE_STRUCT_MEMBER(char, sectname, [16])
+ PROCESS_TYPE_STRUCT_MEMBER(char, segname, [16])
+
+ PROCESS_TYPE_STRUCT_MEMBER(ULong, addr)
+ PROCESS_TYPE_STRUCT_MEMBER(ULong, size)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, offset)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, align)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, reloff)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nreloc)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, flags)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, reserved1)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, reserved2)
+ PROCESS_TYPE_STRUCT_MEMBER(Reserved64Only, reserved3)
+PROCESS_TYPE_STRUCT_END(section)
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/nlist.proctype b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/nlist.proctype
new file mode 100644
index 00000000000..c8989139541
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/nlist.proctype
@@ -0,0 +1,30 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This file corresponds to the system’s <mach-o/nlist.h>.
+//
+// This file is intended to be included multiple times in the same translation
+// unit, so #include guards are intentionally absent.
+//
+// This file is included by snapshot/mac/process_types.h and
+// snapshot/mac/process_types.cc to produce process type struct definitions and
+// accessors.
+
+PROCESS_TYPE_STRUCT_BEGIN(nlist)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, n_strx) // n_un.n_strx
+ PROCESS_TYPE_STRUCT_MEMBER(uint8_t, n_type)
+ PROCESS_TYPE_STRUCT_MEMBER(uint8_t, n_sect)
+ PROCESS_TYPE_STRUCT_MEMBER(uint16_t, n_desc) // 32-bit: int16_t
+ PROCESS_TYPE_STRUCT_MEMBER(ULong, n_value)
+PROCESS_TYPE_STRUCT_END(nlist)
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/traits.h b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/traits.h
new file mode 100644
index 00000000000..0b4d2731a19
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types/traits.h
@@ -0,0 +1,42 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This file is intended to be included multiple times in the same translation
+// unit, so #include guards are intentionally absent.
+//
+// This file is included by snapshot/mac/process_types.h and
+// snapshot/mac/process_types/internal.h to produce traits class definitions.
+
+// Things that #include this file should #undef
+// DECLARE_PROCESS_TYPE_TRAITS_CLASS before #including this file again and after
+// the last #include of this file.
+//
+// |Reserved| is used for padding fields that may be zero-length, and thus
+// __VA_ARGS__, which is intended to set the alignment of the 64-bit types, is
+// not used for that type alias.
+#define DECLARE_PROCESS_TYPE_TRAITS_CLASS(traits_name, lp_bits, ...) \
+ namespace crashpad { \
+ namespace process_types { \
+ namespace internal { \
+ struct Traits##traits_name { \
+ using Long = int##lp_bits##_t __VA_ARGS__; \
+ using ULong = uint##lp_bits##_t __VA_ARGS__; \
+ using Pointer = uint##lp_bits##_t __VA_ARGS__; \
+ using IntPtr = int##lp_bits##_t __VA_ARGS__; \
+ using UIntPtr = uint##lp_bits##_t __VA_ARGS__; \
+ using Reserved64Only = Reserved64Only##lp_bits; \
+ }; \
+ } \
+ } \
+ }
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types_test.cc
new file mode 100644
index 00000000000..b0fe5c65538
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/process_types_test.cc
@@ -0,0 +1,264 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/mac/process_types.h"
+
+#include <AvailabilityMacros.h>
+#include <mach/mach.h>
+#include <string.h>
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+#include "test/mac/dyld.h"
+#include "util/mac/mac_util.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+#define TEST_STRING(process_reader, self_view, proctype_view, field) \
+ do { \
+ if (self_view->field) { \
+ std::string proctype_string; \
+ ASSERT_TRUE(process_reader.Memory()->ReadCString(proctype_view.field, \
+ &proctype_string)); \
+ EXPECT_EQ(self_view->field, proctype_string); \
+ } \
+ } while (false)
+
+TEST(ProcessTypes, DyldImagesSelf) {
+ // Get the in-process view of dyld_all_image_infos, and check it for sanity.
+ const struct dyld_all_image_infos* self_image_infos =
+ _dyld_get_all_image_infos();
+ int mac_os_x_minor_version = MacOSXMinorVersion();
+ if (mac_os_x_minor_version >= 9) {
+ EXPECT_GE(self_image_infos->version, 13u);
+ } else if (mac_os_x_minor_version >= 7) {
+ EXPECT_GE(self_image_infos->version, 8u);
+ } else if (mac_os_x_minor_version >= 6) {
+ EXPECT_GE(self_image_infos->version, 2u);
+ } else {
+ EXPECT_GE(self_image_infos->version, 1u);
+ }
+
+ EXPECT_GT(self_image_infos->infoArrayCount, 1u);
+ if (self_image_infos->version >= 2) {
+ EXPECT_TRUE(self_image_infos->libSystemInitialized);
+ }
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
+ if (self_image_infos->version >= 9) {
+ EXPECT_EQ(self_image_infos, self_image_infos->dyldAllImageInfosAddress);
+ }
+#endif
+
+ // Get the out-of-process view of dyld_all_image_infos, and work with it
+ // through the process_types interface.
+ task_dyld_info_data_t dyld_info;
+ mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+ kern_return_t kr = task_info(mach_task_self(),
+ TASK_DYLD_INFO,
+ reinterpret_cast<task_info_t>(&dyld_info),
+ &count);
+ ASSERT_EQ(KERN_SUCCESS, kr);
+
+ EXPECT_EQ(reinterpret_cast<mach_vm_address_t>(self_image_infos),
+ dyld_info.all_image_info_addr);
+ EXPECT_GT(dyld_info.all_image_info_size, 1u);
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
+#if !defined(ARCH_CPU_64_BITS)
+ EXPECT_EQ(TASK_DYLD_ALL_IMAGE_INFO_32, dyld_info.all_image_info_format);
+#else
+ EXPECT_EQ(TASK_DYLD_ALL_IMAGE_INFO_64, dyld_info.all_image_info_format);
+#endif
+#endif
+
+ ProcessReader process_reader;
+ ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
+
+ process_types::dyld_all_image_infos proctype_image_infos;
+ ASSERT_TRUE(proctype_image_infos.Read(&process_reader,
+ dyld_info.all_image_info_addr));
+
+ ASSERT_EQ(self_image_infos->version, proctype_image_infos.version);
+
+ if (proctype_image_infos.version >= 1) {
+ EXPECT_EQ(self_image_infos->infoArrayCount,
+ proctype_image_infos.infoArrayCount);
+ EXPECT_EQ(reinterpret_cast<uint64_t>(self_image_infos->infoArray),
+ proctype_image_infos.infoArray);
+ EXPECT_EQ(reinterpret_cast<uint64_t>(self_image_infos->notification),
+ proctype_image_infos.notification);
+ EXPECT_EQ(self_image_infos->processDetachedFromSharedRegion,
+ proctype_image_infos.processDetachedFromSharedRegion);
+ }
+ if (proctype_image_infos.version >= 2) {
+ EXPECT_EQ(self_image_infos->libSystemInitialized,
+ proctype_image_infos.libSystemInitialized);
+ EXPECT_EQ(
+ reinterpret_cast<uint64_t>(self_image_infos->dyldImageLoadAddress),
+ proctype_image_infos.dyldImageLoadAddress);
+ }
+ if (proctype_image_infos.version >= 3) {
+ EXPECT_EQ(reinterpret_cast<uint64_t>(self_image_infos->jitInfo),
+ proctype_image_infos.jitInfo);
+ }
+ if (proctype_image_infos.version >= 5) {
+ EXPECT_EQ(reinterpret_cast<uint64_t>(self_image_infos->dyldVersion),
+ proctype_image_infos.dyldVersion);
+ EXPECT_EQ(reinterpret_cast<uint64_t>(self_image_infos->errorMessage),
+ proctype_image_infos.errorMessage);
+ EXPECT_EQ(implicit_cast<uint64_t>(self_image_infos->terminationFlags),
+ proctype_image_infos.terminationFlags);
+
+ TEST_STRING(
+ process_reader, self_image_infos, proctype_image_infos, dyldVersion);
+ TEST_STRING(
+ process_reader, self_image_infos, proctype_image_infos, errorMessage);
+ }
+ if (proctype_image_infos.version >= 6) {
+ EXPECT_EQ(
+ reinterpret_cast<uint64_t>(self_image_infos->coreSymbolicationShmPage),
+ proctype_image_infos.coreSymbolicationShmPage);
+ }
+ if (proctype_image_infos.version >= 7) {
+ EXPECT_EQ(implicit_cast<uint64_t>(self_image_infos->systemOrderFlag),
+ proctype_image_infos.systemOrderFlag);
+ }
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
+ if (proctype_image_infos.version >= 8) {
+ EXPECT_EQ(implicit_cast<uint64_t>(self_image_infos->uuidArrayCount),
+ proctype_image_infos.uuidArrayCount);
+ }
+ if (proctype_image_infos.version >= 9) {
+ EXPECT_EQ(
+ reinterpret_cast<uint64_t>(self_image_infos->dyldAllImageInfosAddress),
+ proctype_image_infos.dyldAllImageInfosAddress);
+ }
+ if (proctype_image_infos.version >= 10) {
+ EXPECT_EQ(implicit_cast<uint64_t>(self_image_infos->initialImageCount),
+ proctype_image_infos.initialImageCount);
+ }
+ if (proctype_image_infos.version >= 11) {
+ EXPECT_EQ(implicit_cast<uint64_t>(self_image_infos->errorKind),
+ proctype_image_infos.errorKind);
+ EXPECT_EQ(
+ reinterpret_cast<uint64_t>(self_image_infos->errorClientOfDylibPath),
+ proctype_image_infos.errorClientOfDylibPath);
+ EXPECT_EQ(
+ reinterpret_cast<uint64_t>(self_image_infos->errorTargetDylibPath),
+ proctype_image_infos.errorTargetDylibPath);
+ EXPECT_EQ(reinterpret_cast<uint64_t>(self_image_infos->errorSymbol),
+ proctype_image_infos.errorSymbol);
+
+ TEST_STRING(process_reader,
+ self_image_infos,
+ proctype_image_infos,
+ errorClientOfDylibPath);
+ TEST_STRING(process_reader,
+ self_image_infos,
+ proctype_image_infos,
+ errorTargetDylibPath);
+ TEST_STRING(
+ process_reader, self_image_infos, proctype_image_infos, errorSymbol);
+ }
+ if (proctype_image_infos.version >= 12) {
+ EXPECT_EQ(implicit_cast<uint64_t>(self_image_infos->sharedCacheSlide),
+ proctype_image_infos.sharedCacheSlide);
+ }
+#endif
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
+ if (proctype_image_infos.version >= 13) {
+ EXPECT_EQ(0,
+ memcmp(self_image_infos->sharedCacheUUID,
+ proctype_image_infos.sharedCacheUUID,
+ sizeof(self_image_infos->sharedCacheUUID)));
+ }
+ if (proctype_image_infos.version >= 14) {
+ for (size_t index = 0; index < arraysize(self_image_infos->reserved);
+ ++index) {
+ EXPECT_EQ(implicit_cast<uint64_t>(self_image_infos->reserved[index]),
+ proctype_image_infos.reserved[index])
+ << "index " << index;
+ }
+ }
+#endif
+
+ if (proctype_image_infos.version >= 1) {
+ std::vector<process_types::dyld_image_info> proctype_image_info_vector(
+ proctype_image_infos.infoArrayCount);
+ ASSERT_TRUE(process_types::dyld_image_info::ReadArrayInto(
+ &process_reader,
+ proctype_image_infos.infoArray,
+ proctype_image_info_vector.size(),
+ &proctype_image_info_vector[0]));
+
+ for (size_t index = 0; index < proctype_image_infos.infoArrayCount;
+ ++index) {
+ const dyld_image_info* self_image_info =
+ &self_image_infos->infoArray[index];
+ const process_types::dyld_image_info& proctype_image_info =
+ proctype_image_info_vector[index];
+
+ EXPECT_EQ(reinterpret_cast<uint64_t>(self_image_info->imageLoadAddress),
+ proctype_image_info.imageLoadAddress)
+ << "index " << index;
+ EXPECT_EQ(reinterpret_cast<uint64_t>(self_image_info->imageFilePath),
+ proctype_image_info.imageFilePath)
+ << "index " << index;
+ EXPECT_EQ(implicit_cast<uint64_t>(self_image_info->imageFileModDate),
+ proctype_image_info.imageFileModDate)
+ << "index " << index;
+
+ TEST_STRING(
+ process_reader, self_image_info, proctype_image_info, imageFilePath);
+ }
+ }
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
+ if (proctype_image_infos.version >= 8) {
+ std::vector<process_types::dyld_uuid_info> proctype_uuid_info_vector(
+ proctype_image_infos.uuidArrayCount);
+ ASSERT_TRUE(process_types::dyld_uuid_info::ReadArrayInto(
+ &process_reader,
+ proctype_image_infos.uuidArray,
+ proctype_uuid_info_vector.size(),
+ &proctype_uuid_info_vector[0]));
+
+ for (size_t index = 0; index < proctype_image_infos.uuidArrayCount;
+ ++index) {
+ const dyld_uuid_info* self_uuid_info =
+ &self_image_infos->uuidArray[index];
+ const process_types::dyld_uuid_info& proctype_uuid_info =
+ proctype_uuid_info_vector[index];
+
+ EXPECT_EQ(reinterpret_cast<uint64_t>(self_uuid_info->imageLoadAddress),
+ proctype_uuid_info.imageLoadAddress)
+ << "index " << index;
+ EXPECT_EQ(0,
+ memcmp(self_uuid_info->imageUUID,
+ proctype_uuid_info.imageUUID,
+ sizeof(self_uuid_info->imageUUID)))
+ << "index " << index;
+ }
+ }
+#endif
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/system_snapshot_mac.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/system_snapshot_mac.cc
new file mode 100644
index 00000000000..185372dd29a
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/system_snapshot_mac.cc
@@ -0,0 +1,399 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/mac/system_snapshot_mac.h"
+
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <time.h>
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "build/build_config.h"
+#include "snapshot/cpu_context.h"
+#include "snapshot/mac/process_reader.h"
+#include "util/mac/mac_util.h"
+#include "util/numeric/in_range_cast.h"
+
+namespace crashpad {
+
+namespace {
+
+template <typename T>
+T ReadIntSysctlByName(const char* name, T default_value) {
+ T value;
+ size_t value_len = sizeof(value);
+ if (sysctlbyname(name, &value, &value_len, nullptr, 0) != 0) {
+ PLOG(WARNING) << "sysctlbyname " << name;
+ return default_value;
+ }
+
+ return value;
+}
+
+template <typename T>
+T CastIntSysctlByName(const char* name, T default_value) {
+ int int_value = ReadIntSysctlByName<int>(name, default_value);
+ return InRangeCast<T>(int_value, default_value);
+}
+
+std::string ReadStringSysctlByName(const char* name) {
+ size_t buf_len;
+ if (sysctlbyname(name, nullptr, &buf_len, nullptr, 0) != 0) {
+ PLOG(WARNING) << "sysctlbyname (size) " << name;
+ return std::string();
+ }
+
+ if (buf_len == 0) {
+ return std::string();
+ }
+
+ std::string value(buf_len - 1, '\0');
+ if (sysctlbyname(name, &value[0], &buf_len, nullptr, 0) != 0) {
+ PLOG(WARNING) << "sysctlbyname " << name;
+ return std::string();
+ }
+
+ return value;
+}
+
+#if defined(ARCH_CPU_X86_FAMILY)
+void CallCPUID(uint32_t leaf,
+ uint32_t* eax,
+ uint32_t* ebx,
+ uint32_t* ecx,
+ uint32_t* edx) {
+ asm("cpuid"
+ : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
+ : "a"(leaf), "b"(0), "c"(0), "d"(0));
+}
+#endif
+
+} // namespace
+
+namespace internal {
+
+SystemSnapshotMac::SystemSnapshotMac()
+ : SystemSnapshot(),
+ os_version_full_(),
+ os_version_build_(),
+ process_reader_(nullptr),
+ snapshot_time_(nullptr),
+ os_version_major_(0),
+ os_version_minor_(0),
+ os_version_bugfix_(0),
+ os_server_(false),
+ initialized_() {
+}
+
+SystemSnapshotMac::~SystemSnapshotMac() {
+}
+
+void SystemSnapshotMac::Initialize(ProcessReader* process_reader,
+ const timeval* snapshot_time) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ process_reader_ = process_reader;
+ snapshot_time_ = snapshot_time;
+
+ // MacOSXVersion() logs its own warnings if it can’t figure anything out. It’s
+ // not fatal if this happens. The default values are reasonable.
+ std::string os_version_string;
+ MacOSXVersion(&os_version_major_,
+ &os_version_minor_,
+ &os_version_bugfix_,
+ &os_version_build_,
+ &os_server_,
+ &os_version_string);
+
+ std::string uname_string;
+ utsname uts;
+ if (uname(&uts) != 0) {
+ PLOG(WARNING) << "uname";
+ } else {
+ uname_string = base::StringPrintf(
+ "%s %s %s %s", uts.sysname, uts.release, uts.version, uts.machine);
+ }
+
+ if (!os_version_string.empty()) {
+ if (!uname_string.empty()) {
+ os_version_full_ = base::StringPrintf(
+ "%s; %s", os_version_string.c_str(), uname_string.c_str());
+ } else {
+ os_version_full_ = os_version_string;
+ }
+ } else {
+ os_version_full_ = uname_string;
+ }
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+}
+
+CPUArchitecture SystemSnapshotMac::GetCPUArchitecture() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+#if defined(ARCH_CPU_X86_FAMILY)
+ return process_reader_->Is64Bit() ? kCPUArchitectureX86_64
+ : kCPUArchitectureX86;
+#else
+#error port to your architecture
+#endif
+}
+
+uint32_t SystemSnapshotMac::CPURevision() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+#if defined(ARCH_CPU_X86_FAMILY)
+ // machdep.cpu.family and machdep.cpu.model already take the extended family
+ // and model IDs into account. See 10.9.2 xnu-2422.90.20/osfmk/i386/cpuid.c
+ // cpuid_set_generic_info().
+ uint16_t family = CastIntSysctlByName<uint16_t>("machdep.cpu.family", 0);
+ uint8_t model = CastIntSysctlByName<uint8_t>("machdep.cpu.model", 0);
+ uint8_t stepping = CastIntSysctlByName<uint8_t>("machdep.cpu.stepping", 0);
+
+ return (family << 16) | (model << 8) | stepping;
+#else
+#error port to your architecture
+#endif
+}
+
+uint8_t SystemSnapshotMac::CPUCount() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return CastIntSysctlByName<uint8_t>("hw.ncpu", 1);
+}
+
+std::string SystemSnapshotMac::CPUVendor() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+#if defined(ARCH_CPU_X86_FAMILY)
+ return ReadStringSysctlByName("machdep.cpu.vendor");
+#else
+#error port to your architecture
+#endif
+}
+
+void SystemSnapshotMac::CPUFrequency(
+ uint64_t* current_hz, uint64_t* max_hz) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *current_hz = ReadIntSysctlByName<uint64_t>("hw.cpufrequency", 0);
+ *max_hz = ReadIntSysctlByName<uint64_t>("hw.cpufrequency_max", 0);
+}
+
+uint32_t SystemSnapshotMac::CPUX86Signature() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+#if defined(ARCH_CPU_X86_FAMILY)
+ return ReadIntSysctlByName<uint32_t>("machdep.cpu.signature", 0);
+#else
+ NOTREACHED();
+ return 0;
+#endif
+}
+
+uint64_t SystemSnapshotMac::CPUX86Features() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+#if defined(ARCH_CPU_X86_FAMILY)
+ return ReadIntSysctlByName<uint64_t>("machdep.cpu.feature_bits", 0);
+#else
+ NOTREACHED();
+ return 0;
+#endif
+}
+
+uint64_t SystemSnapshotMac::CPUX86ExtendedFeatures() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+#if defined(ARCH_CPU_X86_FAMILY)
+ return ReadIntSysctlByName<uint64_t>("machdep.cpu.extfeature_bits", 0);
+#else
+ NOTREACHED();
+ return 0;
+#endif
+}
+
+uint32_t SystemSnapshotMac::CPUX86Leaf7Features() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+#if defined(ARCH_CPU_X86_FAMILY)
+ // The machdep.cpu.leaf7_feature_bits sysctl isn’t supported prior to Mac OS X
+ // 10.7, so read this by calling cpuid directly.
+ //
+ // machdep.cpu.max_basic could be used to check whether to read the leaf, but
+ // that sysctl isn’t supported prior to Mac OS X 10.6, so read the maximum
+ // basic leaf by calling cpuid directly as well. All CPUs that Apple is known
+ // to have shipped should support a maximum basic leaf value of at least 0xa.
+ uint32_t eax, ebx, ecx, edx;
+ CallCPUID(0, &eax, &ebx, &ecx, &edx);
+ if (eax < 7) {
+ return 0;
+ }
+
+ CallCPUID(7, &eax, &ebx, &ecx, &edx);
+ return ebx;
+#else
+ NOTREACHED();
+ return 0;
+#endif
+}
+
+bool SystemSnapshotMac::CPUX86SupportsDAZ() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+#if defined(ARCH_CPU_X86_FAMILY)
+ // The correct way to check for denormals-as-zeros (DAZ) support is to examine
+ // mxcsr mask, which can be done with fxsave. See Intel Software Developer’s
+ // Manual, Volume 1: Basic Architecture (253665-051), 11.6.3 “Checking for the
+ // DAZ Flag in the MXCSR Register”. Note that since this function tests for
+ // DAZ support in the CPU, it checks the mxcsr mask. Testing mxcsr would
+ // indicate whether DAZ is actually enabled, which is a per-thread context
+ // concern.
+ //
+ // All CPUs that Apple is known to have shipped should support DAZ.
+
+ // Test for fxsave support.
+ uint64_t features = CPUX86Features();
+ if (!(features & (UINT64_C(1) << 24))) {
+ return false;
+ }
+
+ // Call fxsave.
+#if defined(ARCH_CPU_X86)
+ CPUContextX86::Fxsave fxsave __attribute__((aligned(16))) = {};
+#elif defined(ARCH_CPU_X86_64)
+ CPUContextX86_64::Fxsave fxsave __attribute__((aligned(16))) = {};
+#endif
+ static_assert(sizeof(fxsave) == 512, "fxsave size");
+ static_assert(offsetof(decltype(fxsave), mxcsr_mask) == 28,
+ "mxcsr_mask offset");
+ asm("fxsave %0" : "=m"(fxsave));
+
+ // Test the DAZ bit.
+ return fxsave.mxcsr_mask & (1 << 6);
+#else
+ NOTREACHED();
+ return false;
+#endif
+}
+
+SystemSnapshot::OperatingSystem SystemSnapshotMac::GetOperatingSystem() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return kOperatingSystemMacOSX;
+}
+
+bool SystemSnapshotMac::OSServer() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return os_server_;
+}
+
+void SystemSnapshotMac::OSVersion(int* major,
+ int* minor,
+ int* bugfix,
+ std::string* build) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *major = os_version_major_;
+ *minor = os_version_minor_;
+ *bugfix = os_version_bugfix_;
+ build->assign(os_version_build_);
+}
+
+std::string SystemSnapshotMac::OSVersionFull() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return os_version_full_;
+}
+
+std::string SystemSnapshotMac::MachineDescription() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ std::string model;
+ std::string board_id;
+ MacModelAndBoard(&model, &board_id);
+
+ if (!model.empty()) {
+ if (!board_id.empty()) {
+ return base::StringPrintf("%s (%s)", model.c_str(), board_id.c_str());
+ }
+ return model;
+ }
+ if (!board_id.empty()) {
+ return base::StringPrintf("(%s)", board_id.c_str());
+ }
+ return std::string();
+}
+
+bool SystemSnapshotMac::NXEnabled() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return ReadIntSysctlByName<int>("kern.nx", 0);
+}
+
+void SystemSnapshotMac::TimeZone(DaylightSavingTimeStatus* dst_status,
+ int* standard_offset_seconds,
+ int* daylight_offset_seconds,
+ std::string* standard_name,
+ std::string* daylight_name) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ tm local;
+ PCHECK(localtime_r(&snapshot_time_->tv_sec, &local)) << "localtime_r";
+
+ *standard_name = tzname[0];
+ if (daylight) {
+ // Scan forward and backward, one month at a time, looking for an instance
+ // when the observance of daylight saving time is different than it is in
+ // |local|.
+ long probe_gmtoff = local.tm_gmtoff;
+
+ const int kMonthDeltas[] =
+ { 0, 1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6,
+ 7, -7, 8, -8, 9, -9, 10, -10, 11, -11, 12, -12 };
+ for (size_t index = 0; index < arraysize(kMonthDeltas); ++index) {
+ // Look at the 15th day of each month at local noon. Set tm_isdst to -1 to
+ // avoid giving mktime() any hints about whether to consider daylight
+ // saving time in effect. mktime() accepts values of tm_mon that are
+ // outside of its normal range and behaves as expected: if tm_mon is -1,
+ // it references December of the preceding year, and if it is 12, it
+ // references January of the following year.
+ tm probe_tm = {};
+ probe_tm.tm_hour = 12;
+ probe_tm.tm_mday = 15;
+ probe_tm.tm_mon = local.tm_mon + kMonthDeltas[index];
+ probe_tm.tm_year = local.tm_year;
+ probe_tm.tm_isdst = -1;
+ if (mktime(&probe_tm) != -1 && probe_tm.tm_isdst != local.tm_isdst) {
+ probe_gmtoff = probe_tm.tm_gmtoff;
+ break;
+ }
+ }
+
+ *daylight_name = tzname[1];
+ if (!local.tm_isdst) {
+ *dst_status = kObservingStandardTime;
+ *standard_offset_seconds = local.tm_gmtoff;
+ *daylight_offset_seconds = probe_gmtoff;
+ } else {
+ *dst_status = kObservingDaylightSavingTime;
+ *standard_offset_seconds = probe_gmtoff;
+ *daylight_offset_seconds = local.tm_gmtoff;
+ }
+ } else {
+ *daylight_name = tzname[0];
+ *dst_status = kDoesNotObserveDaylightSavingTime;
+ *standard_offset_seconds = local.tm_gmtoff;
+ *daylight_offset_seconds = local.tm_gmtoff;
+ }
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/system_snapshot_mac.h b/chromium/third_party/crashpad/crashpad/snapshot/mac/system_snapshot_mac.h
new file mode 100644
index 00000000000..edbd74fb966
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/system_snapshot_mac.h
@@ -0,0 +1,101 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MAC_SYSTEM_SNAPSHOT_MAC_H_
+#define CRASHPAD_SNAPSHOT_MAC_SYSTEM_SNAPSHOT_MAC_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "snapshot/system_snapshot.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+
+class ProcessReader;
+
+namespace internal {
+
+//! \brief A SystemSnapshot of the running system, when the system runs Mac OS
+//! X.
+class SystemSnapshotMac final : public SystemSnapshot {
+ public:
+ SystemSnapshotMac();
+ ~SystemSnapshotMac() override;
+
+ //! \brief Initializes the object.
+ //!
+ //! \param[in] process_reader A reader for the process being snapshotted.
+ //! \n\n
+ //! It seems odd that a system snapshot implementation would need a
+ //! ProcessReader, but some of the information reported about the system
+ //! depends on the process it’s being reported for. For example, the
+ //! architecture returned by GetCPUArchitecture() should be the
+ //! architecture of the process, which may be different than the native
+ //! architecture of the system: an x86_64 system can run both x86_64 and
+ //! 32-bit x86 processes.
+ //! \param[in] snapshot_time The time of the snapshot being taken.
+ //! \n\n
+ //! This parameter is necessary for TimeZone() to determine whether
+ //! daylight saving time was in effect at the time the snapshot was taken.
+ //! Otherwise, it would need to base its determination on the current
+ //! time, which may be different than the snapshot time for snapshots
+ //! generated around the daylight saving transition time.
+ void Initialize(ProcessReader* process_reader, const timeval* snapshot_time);
+
+ // SystemSnapshot:
+
+ CPUArchitecture GetCPUArchitecture() const override;
+ uint32_t CPURevision() const override;
+ uint8_t CPUCount() const override;
+ std::string CPUVendor() const override;
+ void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override;
+ uint32_t CPUX86Signature() const override;
+ uint64_t CPUX86Features() const override;
+ uint64_t CPUX86ExtendedFeatures() const override;
+ uint32_t CPUX86Leaf7Features() const override;
+ bool CPUX86SupportsDAZ() const override;
+ OperatingSystem GetOperatingSystem() const override;
+ bool OSServer() const override;
+ void OSVersion(
+ int* major, int* minor, int* bugfix, std::string* build) const override;
+ std::string OSVersionFull() const override;
+ bool NXEnabled() const override;
+ std::string MachineDescription() const override;
+ void TimeZone(DaylightSavingTimeStatus* dst_status,
+ int* standard_offset_seconds,
+ int* daylight_offset_seconds,
+ std::string* standard_name,
+ std::string* daylight_name) const override;
+
+ private:
+ std::string os_version_full_;
+ std::string os_version_build_;
+ ProcessReader* process_reader_; // weak
+ const timeval* snapshot_time_; // weak
+ int os_version_major_;
+ int os_version_minor_;
+ int os_version_bugfix_;
+ bool os_server_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(SystemSnapshotMac);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MAC_SYSTEM_SNAPSHOT_MAC_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/system_snapshot_mac_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/system_snapshot_mac_test.cc
new file mode 100644
index 00000000000..a02e94e03fe
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/system_snapshot_mac_test.cc
@@ -0,0 +1,176 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/mac/system_snapshot_mac.h"
+
+#include <sys/time.h>
+#include <time.h>
+
+#include <string>
+
+#include "build/build_config.h"
+#include "gtest/gtest.h"
+#include "snapshot/mac/process_reader.h"
+#include "test/errors.h"
+#include "util/mac/mac_util.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+// SystemSnapshotMac objects would be cumbersome to construct in each test that
+// requires one, because of the repetitive and mechanical work necessary to set
+// up a ProcessReader and timeval, along with the checks to verify that these
+// operations succeed. This test fixture class handles the initialization work
+// so that individual tests don’t have to.
+class SystemSnapshotMacTest : public testing::Test {
+ public:
+ SystemSnapshotMacTest()
+ : Test(),
+ process_reader_(),
+ snapshot_time_(),
+ system_snapshot_() {
+ }
+
+ const internal::SystemSnapshotMac& system_snapshot() const {
+ return system_snapshot_;
+ }
+
+ // testing::Test:
+ void SetUp() override {
+ ASSERT_TRUE(process_reader_.Initialize(mach_task_self()));
+ ASSERT_EQ(0, gettimeofday(&snapshot_time_, nullptr))
+ << ErrnoMessage("gettimeofday");
+ system_snapshot_.Initialize(&process_reader_, &snapshot_time_);
+ }
+
+ private:
+ ProcessReader process_reader_;
+ timeval snapshot_time_;
+ internal::SystemSnapshotMac system_snapshot_;
+
+ DISALLOW_COPY_AND_ASSIGN(SystemSnapshotMacTest);
+};
+
+TEST_F(SystemSnapshotMacTest, GetCPUArchitecture) {
+ CPUArchitecture cpu_architecture = system_snapshot().GetCPUArchitecture();
+
+#if defined(ARCH_CPU_X86)
+ EXPECT_EQ(kCPUArchitectureX86, cpu_architecture);
+#elif defined(ARCH_CPU_X86_64)
+ EXPECT_EQ(kCPUArchitectureX86_64, cpu_architecture);
+#else
+#error port to your architecture
+#endif
+}
+
+TEST_F(SystemSnapshotMacTest, CPUCount) {
+ EXPECT_GE(system_snapshot().CPUCount(), 1);
+}
+
+TEST_F(SystemSnapshotMacTest, CPUVendor) {
+ std::string cpu_vendor = system_snapshot().CPUVendor();
+
+#if defined(ARCH_CPU_X86_FAMILY)
+ // Apple has only shipped Intel x86-family CPUs, but here’s a small nod to the
+ // “Hackintosh” crowd.
+ if (cpu_vendor != "GenuineIntel" && cpu_vendor != "AuthenticAMD") {
+ FAIL() << "cpu_vendor " << cpu_vendor;
+ }
+#else
+#error port to your architecture
+#endif
+}
+
+#if defined(ARCH_CPU_X86_FAMILY)
+
+TEST_F(SystemSnapshotMacTest, CPUX86SupportsDAZ) {
+ // All x86-family CPUs that Apple is known to have shipped should support DAZ.
+ EXPECT_TRUE(system_snapshot().CPUX86SupportsDAZ());
+}
+
+#endif
+
+TEST_F(SystemSnapshotMacTest, GetOperatingSystem) {
+ EXPECT_EQ(SystemSnapshot::kOperatingSystemMacOSX,
+ system_snapshot().GetOperatingSystem());
+}
+
+TEST_F(SystemSnapshotMacTest, OSVersion) {
+ int major;
+ int minor;
+ int bugfix;
+ std::string build;
+ system_snapshot().OSVersion(&major, &minor, &bugfix, &build);
+
+ EXPECT_EQ(10, major);
+ EXPECT_EQ(MacOSXMinorVersion(), minor);
+ EXPECT_FALSE(build.empty());
+}
+
+TEST_F(SystemSnapshotMacTest, OSVersionFull) {
+ EXPECT_FALSE(system_snapshot().OSVersionFull().empty());
+}
+
+TEST_F(SystemSnapshotMacTest, MachineDescription) {
+ EXPECT_FALSE(system_snapshot().MachineDescription().empty());
+}
+
+TEST_F(SystemSnapshotMacTest, TimeZone) {
+ SystemSnapshot::DaylightSavingTimeStatus dst_status;
+ int standard_offset_seconds;
+ int daylight_offset_seconds;
+ std::string standard_name;
+ std::string daylight_name;
+
+ system_snapshot().TimeZone(&dst_status,
+ &standard_offset_seconds,
+ &daylight_offset_seconds,
+ &standard_name,
+ &daylight_name);
+
+ // |standard_offset_seconds| gives seconds east of UTC, and |timezone| gives
+ // seconds west of UTC.
+ EXPECT_EQ(-timezone, standard_offset_seconds);
+
+ // In contemporary usage, most time zones have an integer hour offset from
+ // UTC, although several are at a half-hour offset, and two are at 15-minute
+ // offsets. Throughout history, other variations existed. See
+ // http://www.timeanddate.com/time/time-zones-interesting.html.
+ EXPECT_EQ(0, standard_offset_seconds % (15 * 60))
+ << "standard_offset_seconds " << standard_offset_seconds;
+
+ if (dst_status == SystemSnapshot::kDoesNotObserveDaylightSavingTime) {
+ EXPECT_EQ(standard_offset_seconds, daylight_offset_seconds);
+ EXPECT_EQ(standard_name, daylight_name);
+ } else {
+ EXPECT_EQ(0, daylight_offset_seconds % (15 * 60))
+ << "daylight_offset_seconds " << daylight_offset_seconds;
+
+ // In contemporary usage, dst_delta_seconds will almost always be one hour,
+ // except for Lord Howe Island, Australia, which uses a 30-minute
+ // delta. Throughout history, other variations existed. See
+ // http://www.timeanddate.com/time/dst/#brief.
+ int dst_delta_seconds = daylight_offset_seconds - standard_offset_seconds;
+ if (dst_delta_seconds != 60 * 60 && dst_delta_seconds != 30 * 60) {
+ FAIL() << "dst_delta_seconds " << dst_delta_seconds;
+ }
+
+ EXPECT_NE(standard_name, daylight_name);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.cc
new file mode 100644
index 00000000000..0b7607cace2
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.cc
@@ -0,0 +1,115 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/mac/thread_snapshot_mac.h"
+
+#include "base/logging.h"
+#include "snapshot/mac/cpu_context_mac.h"
+#include "snapshot/mac/process_reader.h"
+
+namespace crashpad {
+namespace internal {
+
+ThreadSnapshotMac::ThreadSnapshotMac()
+ : ThreadSnapshot(),
+ context_union_(),
+ context_(),
+ stack_(),
+ thread_id_(0),
+ thread_specific_data_address_(0),
+ thread_(MACH_PORT_NULL),
+ suspend_count_(0),
+ priority_(0),
+ initialized_() {
+}
+
+ThreadSnapshotMac::~ThreadSnapshotMac() {
+}
+
+bool ThreadSnapshotMac::Initialize(
+ ProcessReader* process_reader,
+ const ProcessReader::Thread& process_reader_thread) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ thread_ = process_reader_thread.port;
+ thread_id_ = process_reader_thread.id;
+ suspend_count_ = process_reader_thread.suspend_count;
+ priority_ = process_reader_thread.priority;
+ thread_specific_data_address_ =
+ process_reader_thread.thread_specific_data_address;
+
+ stack_.Initialize(process_reader,
+ process_reader_thread.stack_region_address,
+ process_reader_thread.stack_region_size);
+
+#if defined(ARCH_CPU_X86_FAMILY)
+ if (process_reader->Is64Bit()) {
+ context_.architecture = kCPUArchitectureX86_64;
+ context_.x86_64 = &context_union_.x86_64;
+ InitializeCPUContextX86_64(context_.x86_64,
+ THREAD_STATE_NONE,
+ nullptr,
+ 0,
+ &process_reader_thread.thread_context.t64,
+ &process_reader_thread.float_context.f64,
+ &process_reader_thread.debug_context.d64);
+ } else {
+ context_.architecture = kCPUArchitectureX86;
+ context_.x86 = &context_union_.x86;
+ InitializeCPUContextX86(context_.x86,
+ THREAD_STATE_NONE,
+ nullptr,
+ 0,
+ &process_reader_thread.thread_context.t32,
+ &process_reader_thread.float_context.f32,
+ &process_reader_thread.debug_context.d32);
+ }
+#endif
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+const CPUContext* ThreadSnapshotMac::Context() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return &context_;
+}
+
+const MemorySnapshot* ThreadSnapshotMac::Stack() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return &stack_;
+}
+
+uint64_t ThreadSnapshotMac::ThreadID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return thread_id_;
+}
+
+int ThreadSnapshotMac::SuspendCount() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return suspend_count_;
+}
+
+int ThreadSnapshotMac::Priority() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return priority_;
+}
+
+uint64_t ThreadSnapshotMac::ThreadSpecificDataAddress() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return thread_specific_data_address_;
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.h b/chromium/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.h
new file mode 100644
index 00000000000..a59678cc006
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.h
@@ -0,0 +1,85 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MAC_THREAD_SNAPSHOT_MAC_H_
+#define CRASHPAD_SNAPSHOT_MAC_THREAD_SNAPSHOT_MAC_H_
+
+#include <mach/mach.h>
+#include <stdint.h>
+
+#include "base/basictypes.h"
+#include "build/build_config.h"
+#include "snapshot/cpu_context.h"
+#include "snapshot/mac/memory_snapshot_mac.h"
+#include "snapshot/memory_snapshot.h"
+#include "snapshot/thread_snapshot.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+
+class ProcessReader;
+
+namespace internal {
+
+//! \brief A ThreadSnapshot of a thread in a running (or crashed) process on a
+//! Mac OS X system.
+class ThreadSnapshotMac final : public ThreadSnapshot {
+ public:
+ ThreadSnapshotMac();
+ ~ThreadSnapshotMac() override;
+
+ //! \brief Initializes the object.
+ //!
+ //! \param[in] process_reader A ProcessReader for the task containing the
+ //! thread.
+ //! \param[in] process_reader_thread The thread within the ProcessReader for
+ //! which the snapshot should be created.
+ //!
+ //! \return `true` if the snapshot could be created, `false` otherwise with
+ //! an appropriate message logged.
+ bool Initialize(ProcessReader* process_reader,
+ const ProcessReader::Thread& process_reader_thread);
+
+ // ThreadSnapshot:
+
+ const CPUContext* Context() const override;
+ const MemorySnapshot* Stack() const override;
+ uint64_t ThreadID() const override;
+ int SuspendCount() const override;
+ int Priority() const override;
+ uint64_t ThreadSpecificDataAddress() const override;
+
+ private:
+#if defined(ARCH_CPU_X86_FAMILY)
+ union {
+ CPUContextX86 x86;
+ CPUContextX86_64 x86_64;
+ } context_union_;
+#endif
+ CPUContext context_;
+ MemorySnapshotMac stack_;
+ uint64_t thread_id_;
+ uint64_t thread_specific_data_address_;
+ thread_t thread_;
+ int suspend_count_;
+ int priority_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadSnapshotMac);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MAC_THREAD_SNAPSHOT_MAC_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot.h b/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot.h
new file mode 100644
index 00000000000..47f8443e617
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot.h
@@ -0,0 +1,77 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MEMORY_SNAPSHOT_H_
+#define CRASHPAD_SNAPSHOT_MEMORY_SNAPSHOT_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+namespace crashpad {
+
+//! \brief An abstract interface to a snapshot representing a region of memory
+//! present in a snapshot process.
+class MemorySnapshot {
+ public:
+ //! \brief An interface that MemorySnapshot clients must implement in order to
+ //! receive memory snapshot data.
+ //!
+ //! This callback-based model frees MemorySnapshot implementations from having
+ //! to deal with memory region ownership problems. When a memory snapshot’s
+ //! data is read, it will be passed to a delegate method.
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ //! \brief Called by MemorySnapshot::Read() to provide data requested by a
+ //! call to that method.
+ //!
+ //! \param[in] data A pointer to the data that was read. The callee does not
+ //! take ownership of this data. This data is only valid for the
+ //! duration of the call to this method. This parameter may be `nullptr`
+ //! if \a size is `0`.
+ //! \param[in] size The size of the data that was read.
+ //!
+ //! \return `true` on success, `false` on failure. MemoryDelegate::Read()
+ //! will use this as its own return value.
+ virtual bool MemorySnapshotDelegateRead(void* data, size_t size) = 0;
+ };
+
+ virtual ~MemorySnapshot() {}
+
+ //! \brief The base address of the memory snapshot in the snapshot process’
+ //! address space.
+ virtual uint64_t Address() const = 0;
+
+ //! \brief The size of the memory snapshot.
+ virtual size_t Size() const = 0;
+
+ //! \brief Calls Delegate::MemorySnapshotDelegateRead(), providing it with
+ //! the memory snapshot’s data.
+ //!
+ //! Implementations do not necessarily read the memory snapshot data prior to
+ //! this method being called. Memory snapshot data may be loaded lazily and
+ //! may be discarded after being passed to the delegate. This provides clean
+ //! memory management without burdening a snapshot implementation with the
+ //! requirement that it track all memory region data simultaneously.
+ //!
+ //! \return `false` on failure, otherwise, the return value of
+ //! Delegate::MemorySnapshotDelegateRead(), which should be `true` on
+ //! success and `false` on failure.
+ virtual bool Read(Delegate* delegate) const = 0;
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MEMORY_SNAPSHOT_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_simple_string_dictionary_reader.cc b/chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_simple_string_dictionary_reader.cc
new file mode 100644
index 00000000000..0d8301c1b30
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_simple_string_dictionary_reader.cc
@@ -0,0 +1,86 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/minidump/minidump_simple_string_dictionary_reader.h"
+
+#include <vector>
+#include <utility>
+
+#include "base/logging.h"
+#include "minidump/minidump_extensions.h"
+#include "snapshot/minidump/minidump_string_reader.h"
+
+namespace crashpad {
+namespace internal {
+
+bool ReadMinidumpSimpleStringDictionary(
+ FileReaderInterface* file_reader,
+ const MINIDUMP_LOCATION_DESCRIPTOR& location,
+ std::map<std::string, std::string>* dictionary) {
+ if (location.Rva == 0) {
+ dictionary->clear();
+ return true;
+ }
+
+ if (location.DataSize < sizeof(MinidumpSimpleStringDictionary)) {
+ LOG(ERROR) << "simple_string_dictionary size mismatch";
+ return false;
+ }
+
+ if (!file_reader->SeekSet(location.Rva)) {
+ return false;
+ }
+
+ uint32_t entry_count;
+ if (!file_reader->ReadExactly(&entry_count, sizeof(entry_count))) {
+ return false;
+ }
+
+ if (location.DataSize !=
+ sizeof(MinidumpSimpleStringDictionary) +
+ entry_count * sizeof(MinidumpSimpleStringDictionaryEntry)) {
+ LOG(ERROR) << "simple_string_dictionary size mismatch";
+ return false;
+ }
+
+ std::vector<MinidumpSimpleStringDictionaryEntry> entries(entry_count);
+ if (!file_reader->ReadExactly(&entries[0],
+ entry_count * sizeof(entries[0]))) {
+ return false;
+ }
+
+ std::map<std::string, std::string> local_dictionary;
+ for (const MinidumpSimpleStringDictionaryEntry& entry : entries) {
+ std::string key;
+ if (!ReadMinidumpUTF8String(file_reader, entry.key, &key)) {
+ return false;
+ }
+
+ std::string value;
+ if (!ReadMinidumpUTF8String(file_reader, entry.value, &value)) {
+ return false;
+ }
+
+ if (!local_dictionary.insert(std::make_pair(key, value)).second) {
+ LOG(ERROR) << "duplicate key " << key;
+ return false;
+ }
+ }
+
+ dictionary->swap(local_dictionary);
+ return true;
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_simple_string_dictionary_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_simple_string_dictionary_reader.h
new file mode 100644
index 00000000000..633ea0f1318
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_simple_string_dictionary_reader.h
@@ -0,0 +1,42 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_READER_H_
+#define CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_READER_H_
+
+#include <windows.h>
+#include <dbghelp.h>
+
+#include <map>
+#include <string>
+
+#include "util/file/file_reader.h"
+
+namespace crashpad {
+namespace internal {
+
+//! \brief Reads a MinidumpSimpleStringDictionary from a minidump file \a
+//! location in \a file_reader, and returns it in \a dictionary.
+//!
+//! \return `true` on success, with \a dictionary set by replacing its contents.
+//! `false` on failure, with a message logged.
+bool ReadMinidumpSimpleStringDictionary(
+ FileReaderInterface* file_reader,
+ const MINIDUMP_LOCATION_DESCRIPTOR& location,
+ std::map<std::string, std::string>* dictionary);
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_READER_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_list_reader.cc b/chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_list_reader.cc
new file mode 100644
index 00000000000..9ca6ef02bb2
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_list_reader.cc
@@ -0,0 +1,72 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/minidump/minidump_string_list_reader.h"
+
+#include "base/logging.h"
+#include "minidump/minidump_extensions.h"
+#include "snapshot/minidump/minidump_string_reader.h"
+
+namespace crashpad {
+namespace internal {
+
+bool ReadMinidumpStringList(FileReaderInterface* file_reader,
+ const MINIDUMP_LOCATION_DESCRIPTOR& location,
+ std::vector<std::string>* list) {
+ if (location.Rva == 0) {
+ list->clear();
+ return true;
+ }
+
+ if (location.DataSize < sizeof(MinidumpRVAList)) {
+ LOG(ERROR) << "string_list size mismatch";
+ return false;
+ }
+
+ if (!file_reader->SeekSet(location.Rva)) {
+ return false;
+ }
+
+ uint32_t entry_count;
+ if (!file_reader->ReadExactly(&entry_count, sizeof(entry_count))) {
+ return false;
+ }
+
+ if (location.DataSize !=
+ sizeof(MinidumpRVAList) + entry_count * sizeof(RVA)) {
+ LOG(ERROR) << "string_list size mismatch";
+ return false;
+ }
+
+ std::vector<RVA> rvas(entry_count);
+ if (!file_reader->ReadExactly(&rvas[0], entry_count * sizeof(rvas[0]))) {
+ return false;
+ }
+
+ std::vector<std::string> local_list;
+ for (RVA rva : rvas) {
+ std::string element;
+ if (!ReadMinidumpUTF8String(file_reader, rva, &element)) {
+ return false;
+ }
+
+ local_list.push_back(element);
+ }
+
+ list->swap(local_list);
+ return true;
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_list_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_list_reader.h
new file mode 100644
index 00000000000..2521605a1cf
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_list_reader.h
@@ -0,0 +1,42 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_LIST_READER_H_
+#define CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_LIST_READER_H_
+
+#include <windows.h>
+#include <dbghelp.h>
+
+#include <string>
+#include <vector>
+
+#include "util/file/file_reader.h"
+
+namespace crashpad {
+namespace internal {
+
+//! \brief Reads a list of MinidumpUTF8String objects in a MinidumpRVAList from
+//! a minidump file \a location in \a file_reader, and returns it in \a
+//! list.
+//!
+//! \return `true` on success, with \a list set by replacing its contents.
+//! `false` on failure, with a message logged.
+bool ReadMinidumpStringList(FileReaderInterface* file_reader,
+ const MINIDUMP_LOCATION_DESCRIPTOR& location,
+ std::vector<std::string>* list);
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_LIST_READER_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_reader.cc b/chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_reader.cc
new file mode 100644
index 00000000000..6ea36089f60
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_reader.cc
@@ -0,0 +1,50 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/minidump/minidump_string_reader.h"
+
+#include "base/logging.h"
+#include "minidump/minidump_extensions.h"
+
+namespace crashpad {
+namespace internal {
+
+bool ReadMinidumpUTF8String(FileReaderInterface* file_reader,
+ RVA rva,
+ std::string* string) {
+ if (rva == 0) {
+ string->clear();
+ return true;
+ }
+
+ if (!file_reader->SeekSet(rva)) {
+ return false;
+ }
+
+ uint32_t string_size;
+ if (!file_reader->ReadExactly(&string_size, sizeof(string_size))) {
+ return false;
+ }
+
+ std::string local_string(string_size, '\0');
+ if (!file_reader->ReadExactly(&local_string[0], string_size)) {
+ return false;
+ }
+
+ string->swap(local_string);
+ return true;
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_reader.h
new file mode 100644
index 00000000000..e5667ec242b
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/minidump/minidump_string_reader.h
@@ -0,0 +1,40 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_READER_H_
+#define CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_READER_H_
+
+#include <windows.h>
+#include <dbghelp.h>
+
+#include <string>
+
+#include "util/file/file_reader.h"
+
+namespace crashpad {
+namespace internal {
+
+//! \brief Reads a MinidumpUTF8String from a minidump file at offset \a rva in
+//! \a file_reader, and returns it in \a string.
+//!
+//! \return `true` on success, with \a string set. `false` on failure, with a
+//! message logged.
+bool ReadMinidumpUTF8String(FileReaderInterface* file_reader,
+ RVA rva,
+ std::string* string);
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_READER_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/minidump/module_snapshot_minidump.cc b/chromium/third_party/crashpad/crashpad/snapshot/minidump/module_snapshot_minidump.cc
new file mode 100644
index 00000000000..12a340d5660
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/minidump/module_snapshot_minidump.cc
@@ -0,0 +1,174 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/minidump/module_snapshot_minidump.h"
+
+#include "minidump/minidump_extensions.h"
+#include "snapshot/minidump/minidump_simple_string_dictionary_reader.h"
+#include "snapshot/minidump/minidump_string_list_reader.h"
+
+namespace crashpad {
+namespace internal {
+
+ModuleSnapshotMinidump::ModuleSnapshotMinidump()
+ : ModuleSnapshot(),
+ minidump_module_(),
+ annotations_vector_(),
+ annotations_simple_map_(),
+ initialized_() {
+}
+
+ModuleSnapshotMinidump::~ModuleSnapshotMinidump() {
+}
+
+bool ModuleSnapshotMinidump::Initialize(
+ FileReaderInterface* file_reader,
+ RVA minidump_module_rva,
+ const MINIDUMP_LOCATION_DESCRIPTOR*
+ minidump_module_crashpad_info_location) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ if (!file_reader->SeekSet(minidump_module_rva)) {
+ return false;
+ }
+
+ if (!file_reader->ReadExactly(&minidump_module_, sizeof(minidump_module_))) {
+ return false;
+ }
+
+ if (!InitializeModuleCrashpadInfo(file_reader,
+ minidump_module_crashpad_info_location)) {
+ return false;
+ }
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+std::string ModuleSnapshotMinidump::Name() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
+ return std::string();
+}
+
+uint64_t ModuleSnapshotMinidump::Address() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
+ return 0;
+}
+
+uint64_t ModuleSnapshotMinidump::Size() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
+ return 0;
+}
+
+time_t ModuleSnapshotMinidump::Timestamp() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
+ return 0;
+}
+
+void ModuleSnapshotMinidump::FileVersion(uint16_t* version_0,
+ uint16_t* version_1,
+ uint16_t* version_2,
+ uint16_t* version_3) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
+ *version_0 = 0;
+ *version_1 = 0;
+ *version_2 = 0;
+ *version_3 = 0;
+}
+
+void ModuleSnapshotMinidump::SourceVersion(uint16_t* version_0,
+ uint16_t* version_1,
+ uint16_t* version_2,
+ uint16_t* version_3) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
+ *version_0 = 0;
+ *version_1 = 0;
+ *version_2 = 0;
+ *version_3 = 0;
+}
+
+ModuleSnapshot::ModuleType ModuleSnapshotMinidump::GetModuleType() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
+ return kModuleTypeUnknown;
+}
+
+void ModuleSnapshotMinidump::UUID(crashpad::UUID* uuid) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
+ *uuid = crashpad::UUID();
+}
+
+std::vector<std::string> ModuleSnapshotMinidump::AnnotationsVector() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return annotations_vector_;
+}
+
+std::map<std::string, std::string>
+ModuleSnapshotMinidump::AnnotationsSimpleMap() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return annotations_simple_map_;
+}
+
+bool ModuleSnapshotMinidump::InitializeModuleCrashpadInfo(
+ FileReaderInterface* file_reader,
+ const MINIDUMP_LOCATION_DESCRIPTOR*
+ minidump_module_crashpad_info_location) {
+ if (!minidump_module_crashpad_info_location ||
+ minidump_module_crashpad_info_location->Rva == 0) {
+ return true;
+ }
+
+ MinidumpModuleCrashpadInfo minidump_module_crashpad_info;
+ if (minidump_module_crashpad_info_location->DataSize <
+ sizeof(minidump_module_crashpad_info)) {
+ LOG(ERROR) << "minidump_module_crashpad_info size mismatch";
+ return false;
+ }
+
+ if (!file_reader->SeekSet(minidump_module_crashpad_info_location->Rva)) {
+ return false;
+ }
+
+ if (!file_reader->ReadExactly(&minidump_module_crashpad_info,
+ sizeof(minidump_module_crashpad_info))) {
+ return false;
+ }
+
+ if (minidump_module_crashpad_info.version !=
+ MinidumpModuleCrashpadInfo::kVersion) {
+ LOG(ERROR) << "minidump_module_crashpad_info version mismatch";
+ return false;
+ }
+
+ if (!ReadMinidumpStringList(file_reader,
+ minidump_module_crashpad_info.list_annotations,
+ &annotations_vector_)) {
+ return false;
+ }
+
+ return ReadMinidumpSimpleStringDictionary(
+ file_reader,
+ minidump_module_crashpad_info.simple_annotations,
+ &annotations_simple_map_);
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/minidump/module_snapshot_minidump.h b/chromium/third_party/crashpad/crashpad/snapshot/minidump/module_snapshot_minidump.h
new file mode 100644
index 00000000000..b04d498a7fd
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/minidump/module_snapshot_minidump.h
@@ -0,0 +1,97 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MODULE_SNAPSHOT_MINIDUMP_H_
+#define CRASHPAD_SNAPSHOT_MINIDUMP_MODULE_SNAPSHOT_MINIDUMP_H_
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "snapshot/module_snapshot.h"
+#include "util/file/file_reader.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+namespace internal {
+
+//! \brief A ModuleSnapshot based on a module in a minidump file.
+class ModuleSnapshotMinidump final : public ModuleSnapshot {
+ public:
+ ModuleSnapshotMinidump();
+ ~ModuleSnapshotMinidump() override;
+
+ //! \brief Initializes the object.
+ //!
+ //! \param[in] file_reader A file reader corresponding to a minidump file.
+ //! The file reader must support seeking.
+ //! \param[in] minidump_module_rva The file offset in \a file_reader at which
+ //! the module’s MINIDUMP_MODULE structure is located.
+ //! \param[in] minidump_crashpad_module_info_location The location in \a
+ //! file_reader at which the module’s corresponding
+ //! MinidumpModuleCrashpadInfo structure is located. If no such
+ //! corresponding structure is available for a module, this may be
+ //! `nullptr`.
+ //!
+ //! \return `true` if the snapshot could be created, `false` otherwise with
+ //! an appropriate message logged.
+ bool Initialize(FileReaderInterface* file_reader,
+ RVA minidump_module_rva,
+ const MINIDUMP_LOCATION_DESCRIPTOR*
+ minidump_crashpad_module_info_location);
+
+ // ModuleSnapshot:
+
+ std::string Name() const override;
+ uint64_t Address() const override;
+ uint64_t Size() const override;
+ time_t Timestamp() const override;
+ void FileVersion(uint16_t* version_0,
+ uint16_t* version_1,
+ uint16_t* version_2,
+ uint16_t* version_3) const override;
+ void SourceVersion(uint16_t* version_0,
+ uint16_t* version_1,
+ uint16_t* version_2,
+ uint16_t* version_3) const override;
+ ModuleType GetModuleType() const override;
+ void UUID(crashpad::UUID* uuid) const override;
+ std::vector<std::string> AnnotationsVector() const override;
+ std::map<std::string, std::string> AnnotationsSimpleMap() const override;
+
+ private:
+ // Initializes data carried in a MinidumpModuleCrashpadInfo structure on
+ // behalf of Initialize().
+ bool InitializeModuleCrashpadInfo(FileReaderInterface* file_reader,
+ const MINIDUMP_LOCATION_DESCRIPTOR*
+ minidump_module_crashpad_info_location);
+
+ MINIDUMP_MODULE minidump_module_;
+ std::vector<std::string> annotations_vector_;
+ std::map<std::string, std::string> annotations_simple_map_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotMinidump);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MODULE_SNAPSHOT_MINIDUMP_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.cc b/chromium/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.cc
new file mode 100644
index 00000000000..fa87981b5c2
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.cc
@@ -0,0 +1,325 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/minidump/process_snapshot_minidump.h"
+
+#include <utility>
+
+#include "base/memory/scoped_ptr.h"
+#include "util/file/file_io.h"
+#include "snapshot/minidump/minidump_simple_string_dictionary_reader.h"
+
+namespace crashpad {
+
+ProcessSnapshotMinidump::ProcessSnapshotMinidump()
+ : ProcessSnapshot(),
+ header_(),
+ stream_directory_(),
+ stream_map_(),
+ modules_(),
+ crashpad_info_(),
+ annotations_simple_map_(),
+ file_reader_(nullptr),
+ initialized_() {
+}
+
+ProcessSnapshotMinidump::~ProcessSnapshotMinidump() {
+}
+
+bool ProcessSnapshotMinidump::Initialize(FileReaderInterface* file_reader) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ file_reader_ = file_reader;
+
+ if (!file_reader_->SeekSet(0)) {
+ return false;
+ }
+
+ if (!file_reader_->ReadExactly(&header_, sizeof(header_))) {
+ return false;
+ }
+
+ if (header_.Signature != MINIDUMP_SIGNATURE) {
+ LOG(ERROR) << "minidump signature mismatch";
+ return false;
+ }
+
+ if (header_.Version != MINIDUMP_VERSION) {
+ LOG(ERROR) << "minidump version mismatch";
+ return false;
+ }
+
+ if (!file_reader->SeekSet(header_.StreamDirectoryRva)) {
+ return false;
+ }
+
+ stream_directory_.resize(header_.NumberOfStreams);
+ if (!file_reader_->ReadExactly(
+ &stream_directory_[0],
+ header_.NumberOfStreams * sizeof(stream_directory_[0]))) {
+ return false;
+ }
+
+ for (const MINIDUMP_DIRECTORY& directory : stream_directory_) {
+ const MinidumpStreamType stream_type =
+ static_cast<MinidumpStreamType>(directory.StreamType);
+ if (stream_map_.find(stream_type) != stream_map_.end()) {
+ LOG(ERROR) << "duplicate streams for type " << directory.StreamType;
+ return false;
+ }
+
+ stream_map_[stream_type] = &directory.Location;
+ }
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+
+ if (!InitializeCrashpadInfo()) {
+ return false;
+ }
+
+ return InitializeModules();
+}
+
+pid_t ProcessSnapshotMinidump::ProcessID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
+ return 0;
+}
+
+pid_t ProcessSnapshotMinidump::ParentProcessID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
+ return 0;
+}
+
+void ProcessSnapshotMinidump::SnapshotTime(timeval* snapshot_time) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
+ snapshot_time->tv_sec = 0;
+ snapshot_time->tv_usec = 0;
+}
+
+void ProcessSnapshotMinidump::ProcessStartTime(timeval* start_time) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
+ start_time->tv_sec = 0;
+ start_time->tv_usec = 0;
+}
+
+void ProcessSnapshotMinidump::ProcessCPUTimes(timeval* user_time,
+ timeval* system_time) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
+ user_time->tv_sec = 0;
+ user_time->tv_usec = 0;
+ system_time->tv_sec = 0;
+ system_time->tv_usec = 0;
+}
+
+void ProcessSnapshotMinidump::ReportID(UUID* report_id) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *report_id = crashpad_info_.report_id;
+}
+
+void ProcessSnapshotMinidump::ClientID(UUID* client_id) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *client_id = crashpad_info_.client_id;
+}
+
+const std::map<std::string, std::string>&
+ProcessSnapshotMinidump::AnnotationsSimpleMap() const {
+ // TODO(mark): This method should not be const, although the interface
+ // currently imposes this requirement. Making it non-const would allow
+ // annotations_simple_map_ to be lazily constructed: InitializeCrashpadInfo()
+ // could be called here, and from other locations that require it, rather than
+ // calling it from Initialize().
+ // https://code.google.com/p/crashpad/issues/detail?id=9
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return annotations_simple_map_;
+}
+
+const SystemSnapshot* ProcessSnapshotMinidump::System() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
+ return nullptr;
+}
+
+std::vector<const ThreadSnapshot*> ProcessSnapshotMinidump::Threads() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
+ return std::vector<const ThreadSnapshot*>();
+}
+
+std::vector<const ModuleSnapshot*> ProcessSnapshotMinidump::Modules() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ std::vector<const ModuleSnapshot*> modules;
+ for (internal::ModuleSnapshotMinidump* module : modules_) {
+ modules.push_back(module);
+ }
+ return modules;
+}
+
+const ExceptionSnapshot* ProcessSnapshotMinidump::Exception() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
+ return nullptr;
+}
+
+bool ProcessSnapshotMinidump::InitializeCrashpadInfo() {
+ const auto& stream_it = stream_map_.find(kMinidumpStreamTypeCrashpadInfo);
+ if (stream_it == stream_map_.end()) {
+ return true;
+ }
+
+ if (stream_it->second->DataSize < sizeof(crashpad_info_)) {
+ LOG(ERROR) << "crashpad_info size mismatch";
+ return false;
+ }
+
+ if (!file_reader_->SeekSet(stream_it->second->Rva)) {
+ return false;
+ }
+
+ if (!file_reader_->ReadExactly(&crashpad_info_, sizeof(crashpad_info_))) {
+ return false;
+ }
+
+ if (crashpad_info_.version != MinidumpCrashpadInfo::kVersion) {
+ LOG(ERROR) << "crashpad_info version mismatch";
+ return false;
+ }
+
+ return internal::ReadMinidumpSimpleStringDictionary(
+ file_reader_,
+ crashpad_info_.simple_annotations,
+ &annotations_simple_map_);
+}
+
+bool ProcessSnapshotMinidump::InitializeModules() {
+ const auto& stream_it = stream_map_.find(kMinidumpStreamTypeModuleList);
+ if (stream_it == stream_map_.end()) {
+ return true;
+ }
+
+ std::map<uint32_t, MINIDUMP_LOCATION_DESCRIPTOR> module_crashpad_info_links;
+ if (!InitializeModulesCrashpadInfo(&module_crashpad_info_links)) {
+ return false;
+ }
+
+ if (stream_it->second->DataSize < sizeof(MINIDUMP_MODULE_LIST)) {
+ LOG(ERROR) << "module_list size mismatch";
+ return false;
+ }
+
+ if (!file_reader_->SeekSet(stream_it->second->Rva)) {
+ return false;
+ }
+
+ uint32_t module_count;
+ if (!file_reader_->ReadExactly(&module_count, sizeof(module_count))) {
+ return false;
+ }
+
+ if (sizeof(MINIDUMP_MODULE_LIST) + module_count * sizeof(MINIDUMP_MODULE) !=
+ stream_it->second->DataSize) {
+ LOG(ERROR) << "module_list size mismatch";
+ return false;
+ }
+
+ for (uint32_t module_index = 0; module_index < module_count; ++module_index) {
+ const RVA module_rva = stream_it->second->Rva + sizeof(module_count) +
+ module_index * sizeof(MINIDUMP_MODULE);
+
+ const auto& module_crashpad_info_it =
+ module_crashpad_info_links.find(module_index);
+ const MINIDUMP_LOCATION_DESCRIPTOR* module_crashpad_info_location =
+ module_crashpad_info_it != module_crashpad_info_links.end()
+ ? &module_crashpad_info_it->second
+ : nullptr;
+
+ auto module = make_scoped_ptr(new internal::ModuleSnapshotMinidump());
+ if (!module->Initialize(
+ file_reader_, module_rva, module_crashpad_info_location)) {
+ return false;
+ }
+
+ modules_.push_back(module.release());
+ }
+
+ return true;
+}
+
+bool ProcessSnapshotMinidump::InitializeModulesCrashpadInfo(
+ std::map<uint32_t, MINIDUMP_LOCATION_DESCRIPTOR>*
+ module_crashpad_info_links) {
+ module_crashpad_info_links->clear();
+
+ if (crashpad_info_.version != MinidumpCrashpadInfo::kVersion) {
+ return false;
+ }
+
+ if (crashpad_info_.module_list.Rva == 0) {
+ return true;
+ }
+
+ if (crashpad_info_.module_list.DataSize <
+ sizeof(MinidumpModuleCrashpadInfoList)) {
+ LOG(ERROR) << "module_crashpad_info_list size mismatch";
+ return false;
+ }
+
+ if (!file_reader_->SeekSet(crashpad_info_.module_list.Rva)) {
+ return false;
+ }
+
+ uint32_t crashpad_module_count;
+ if (!file_reader_->ReadExactly(&crashpad_module_count,
+ sizeof(crashpad_module_count))) {
+ return false;
+ }
+
+ if (crashpad_info_.module_list.DataSize !=
+ sizeof(MinidumpModuleCrashpadInfoList) +
+ crashpad_module_count * sizeof(MinidumpModuleCrashpadInfoLink)) {
+ LOG(ERROR) << "module_crashpad_info_list size mismatch";
+ return false;
+ }
+
+ scoped_ptr<MinidumpModuleCrashpadInfoLink[]> minidump_links(
+ new MinidumpModuleCrashpadInfoLink[crashpad_module_count]);
+ if (!file_reader_->ReadExactly(
+ &minidump_links[0],
+ crashpad_module_count * sizeof(MinidumpModuleCrashpadInfoLink))) {
+ return false;
+ }
+
+ for (uint32_t crashpad_module_index = 0;
+ crashpad_module_index < crashpad_module_count;
+ ++crashpad_module_index) {
+ const MinidumpModuleCrashpadInfoLink& minidump_link =
+ minidump_links[crashpad_module_index];
+ if (!module_crashpad_info_links
+ ->insert(std::make_pair(minidump_link.minidump_module_list_index,
+ minidump_link.location)).second) {
+ LOG(WARNING)
+ << "duplicate module_crashpad_info_list minidump_module_list_index "
+ << minidump_link.minidump_module_list_index;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.h b/chromium/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.h
new file mode 100644
index 00000000000..44ce5d56abc
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.h
@@ -0,0 +1,102 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_PROCESS_SNAPSHOT_MINIDUMP_H_
+#define CRASHPAD_SNAPSHOT_MINIDUMP_PROCESS_SNAPSHOT_MINIDUMP_H_
+
+#include <sys/time.h>
+#include <windows.h>
+#include <dbghelp.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "minidump/minidump_extensions.h"
+#include "snapshot/exception_snapshot.h"
+#include "snapshot/minidump/module_snapshot_minidump.h"
+#include "snapshot/module_snapshot.h"
+#include "snapshot/process_snapshot.h"
+#include "snapshot/system_snapshot.h"
+#include "snapshot/thread_snapshot.h"
+#include "util/file/file_reader.h"
+#include "util/misc/initialization_state_dcheck.h"
+#include "util/misc/uuid.h"
+#include "util/stdlib/pointer_container.h"
+
+namespace crashpad {
+
+//! \brief A ProcessSnapshot based on a minidump file.
+class ProcessSnapshotMinidump final : public ProcessSnapshot {
+ public:
+ ProcessSnapshotMinidump();
+ ~ProcessSnapshotMinidump() override;
+
+ //! \brief Initializes the object.
+ //!
+ //! \param[in] file_reader A file reader corresponding to a minidump file.
+ //! The file reader must support seeking.
+ //!
+ //! \return `true` if the snapshot could be created, `false` otherwise with
+ //! an appropriate message logged.
+ bool Initialize(FileReaderInterface* file_reader);
+
+ // ProcessSnapshot:
+
+ pid_t ProcessID() const override;
+ pid_t ParentProcessID() const override;
+ void SnapshotTime(timeval* snapshot_time) const override;
+ void ProcessStartTime(timeval* start_time) const override;
+ void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override;
+ void ReportID(UUID* report_id) const override;
+ void ClientID(UUID* client_id) const override;
+ const std::map<std::string, std::string>& AnnotationsSimpleMap()
+ const override;
+ const SystemSnapshot* System() const override;
+ std::vector<const ThreadSnapshot*> Threads() const override;
+ std::vector<const ModuleSnapshot*> Modules() const override;
+ const ExceptionSnapshot* Exception() const override;
+
+ private:
+ // Initializes data carried in a MinidumpCrashpadInfo stream on behalf of
+ // Initialize().
+ bool InitializeCrashpadInfo();
+
+ // Initializes data carried in a MINIDUMP_MODULE_LIST stream on behalf of
+ // Initialize().
+ bool InitializeModules();
+
+ // Initializes data carried in a MinidumpModuleCrashpadInfoList structure on
+ // behalf of InitializeModules(). This makes use of MinidumpCrashpadInfo as
+ // well, so it must be called after InitializeCrashpadInfo().
+ bool InitializeModulesCrashpadInfo(
+ std::map<uint32_t, MINIDUMP_LOCATION_DESCRIPTOR>*
+ module_crashpad_info_links);
+
+ MINIDUMP_HEADER header_;
+ std::vector<MINIDUMP_DIRECTORY> stream_directory_;
+ std::map<MinidumpStreamType, const MINIDUMP_LOCATION_DESCRIPTOR*> stream_map_;
+ PointerVector<internal::ModuleSnapshotMinidump> modules_;
+ MinidumpCrashpadInfo crashpad_info_;
+ std::map<std::string, std::string> annotations_simple_map_;
+ FileReaderInterface* file_reader_; // weak
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotMinidump);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MINIDUMP_PROCESS_SNAPSHOT_MINIDUMP_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc
new file mode 100644
index 00000000000..8eb7349b3f2
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc
@@ -0,0 +1,338 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/minidump/process_snapshot_minidump.h"
+
+#include <string.h>
+#include <windows.h>
+#include <dbghelp.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "gtest/gtest.h"
+#include "snapshot/module_snapshot.h"
+#include "util/file/string_file.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(ProcessSnapshotMinidump, EmptyFile) {
+ StringFile string_file;
+ ProcessSnapshotMinidump process_snapshot;
+
+ EXPECT_FALSE(process_snapshot.Initialize(&string_file));
+}
+
+TEST(ProcessSnapshotMinidump, InvalidSignatureAndVersion) {
+ StringFile string_file;
+
+ MINIDUMP_HEADER header = {};
+
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ ProcessSnapshotMinidump process_snapshot;
+ EXPECT_FALSE(process_snapshot.Initialize(&string_file));
+}
+
+TEST(ProcessSnapshotMinidump, Empty) {
+ StringFile string_file;
+
+ MINIDUMP_HEADER header = {};
+ header.Signature = MINIDUMP_SIGNATURE;
+ header.Version = MINIDUMP_VERSION;
+
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ ProcessSnapshotMinidump process_snapshot;
+ EXPECT_TRUE(process_snapshot.Initialize(&string_file));
+
+ UUID client_id;
+ process_snapshot.ClientID(&client_id);
+ EXPECT_EQ(UUID(), client_id);
+
+ EXPECT_TRUE(process_snapshot.AnnotationsSimpleMap().empty());
+}
+
+// Writes |string| to |writer| as a MinidumpUTF8String, and returns the file
+// offst of the beginning of the string.
+RVA WriteString(FileWriterInterface* writer, const std::string& string) {
+ RVA rva = static_cast<RVA>(writer->SeekGet());
+
+ uint32_t string_size = static_cast<uint32_t>(string.size());
+ EXPECT_TRUE(writer->Write(&string_size, sizeof(string_size)));
+
+ // Include the trailing NUL character.
+ EXPECT_TRUE(writer->Write(string.c_str(), string.size() + 1));
+
+ return rva;
+}
+
+// Writes |dictionary| to |writer| as a MinidumpSimpleStringDictionary, and
+// populates |location| with a location descriptor identifying what was written.
+void WriteMinidumpSimpleStringDictionary(
+ MINIDUMP_LOCATION_DESCRIPTOR* location,
+ FileWriterInterface* writer,
+ const std::map<std::string, std::string>& dictionary) {
+ std::vector<MinidumpSimpleStringDictionaryEntry> entries;
+ for (const auto& it : dictionary) {
+ MinidumpSimpleStringDictionaryEntry entry;
+ entry.key = WriteString(writer, it.first);
+ entry.value = WriteString(writer, it.second);
+ entries.push_back(entry);
+ }
+
+ location->Rva = static_cast<RVA>(writer->SeekGet());
+
+ const uint32_t simple_string_dictionary_entries =
+ static_cast<uint32_t>(entries.size());
+ EXPECT_TRUE(writer->Write(&simple_string_dictionary_entries,
+ sizeof(simple_string_dictionary_entries)));
+ for (const MinidumpSimpleStringDictionaryEntry& entry : entries) {
+ EXPECT_TRUE(writer->Write(&entry, sizeof(entry)));
+ }
+
+ location->DataSize = static_cast<uint32_t>(
+ sizeof(simple_string_dictionary_entries) +
+ entries.size() * sizeof(MinidumpSimpleStringDictionaryEntry));
+}
+
+// Writes |strings| to |writer| as a MinidumpRVAList referencing
+// MinidumpUTF8String objects, and populates |location| with a location
+// descriptor identifying what was written.
+void WriteMinidumpStringList(MINIDUMP_LOCATION_DESCRIPTOR* location,
+ FileWriterInterface* writer,
+ const std::vector<std::string>& strings) {
+ std::vector<RVA> rvas;
+ for (const std::string& string : strings) {
+ rvas.push_back(WriteString(writer, string));
+ }
+
+ location->Rva = static_cast<RVA>(writer->SeekGet());
+
+ const uint32_t string_list_entries = static_cast<uint32_t>(rvas.size());
+ EXPECT_TRUE(writer->Write(&string_list_entries, sizeof(string_list_entries)));
+ for (RVA rva : rvas) {
+ EXPECT_TRUE(writer->Write(&rva, sizeof(rva)));
+ }
+
+ location->DataSize = static_cast<uint32_t>(sizeof(string_list_entries) +
+ rvas.size() * sizeof(RVA));
+}
+
+TEST(ProcessSnapshotMinidump, ClientID) {
+ StringFile string_file;
+
+ MINIDUMP_HEADER header = {};
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ UUID client_id;
+ ASSERT_TRUE(
+ client_id.InitializeFromString("0001f4a9-d00d-5155-0a55-c0ffeec0ffee"));
+
+ MinidumpCrashpadInfo crashpad_info = {};
+ crashpad_info.version = MinidumpCrashpadInfo::kVersion;
+ crashpad_info.client_id = client_id;
+
+ MINIDUMP_DIRECTORY crashpad_info_directory = {};
+ crashpad_info_directory.StreamType = kMinidumpStreamTypeCrashpadInfo;
+ crashpad_info_directory.Location.Rva =
+ static_cast<RVA>(string_file.SeekGet());
+ EXPECT_TRUE(string_file.Write(&crashpad_info, sizeof(crashpad_info)));
+ crashpad_info_directory.Location.DataSize = sizeof(crashpad_info);
+
+ header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());
+ EXPECT_TRUE(string_file.Write(&crashpad_info_directory,
+ sizeof(crashpad_info_directory)));
+
+ header.Signature = MINIDUMP_SIGNATURE;
+ header.Version = MINIDUMP_VERSION;
+ header.NumberOfStreams = 1;
+ EXPECT_TRUE(string_file.SeekSet(0));
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ ProcessSnapshotMinidump process_snapshot;
+ EXPECT_TRUE(process_snapshot.Initialize(&string_file));
+
+ UUID actual_client_id;
+ process_snapshot.ClientID(&actual_client_id);
+ EXPECT_EQ(client_id, actual_client_id);
+
+ EXPECT_TRUE(process_snapshot.AnnotationsSimpleMap().empty());
+}
+
+TEST(ProcessSnapshotMinidump, AnnotationsSimpleMap) {
+ StringFile string_file;
+
+ MINIDUMP_HEADER header = {};
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ MinidumpCrashpadInfo crashpad_info = {};
+ crashpad_info.version = MinidumpCrashpadInfo::kVersion;
+
+ std::map<std::string, std::string> dictionary;
+ dictionary["the first key"] = "THE FIRST VALUE EVER!";
+ dictionary["2key"] = "a lowly second value";
+ WriteMinidumpSimpleStringDictionary(
+ &crashpad_info.simple_annotations, &string_file, dictionary);
+
+ MINIDUMP_DIRECTORY crashpad_info_directory = {};
+ crashpad_info_directory.StreamType = kMinidumpStreamTypeCrashpadInfo;
+ crashpad_info_directory.Location.Rva =
+ static_cast<RVA>(string_file.SeekGet());
+ EXPECT_TRUE(string_file.Write(&crashpad_info, sizeof(crashpad_info)));
+ crashpad_info_directory.Location.DataSize = sizeof(crashpad_info);
+
+ header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());
+ EXPECT_TRUE(string_file.Write(&crashpad_info_directory,
+ sizeof(crashpad_info_directory)));
+
+ header.Signature = MINIDUMP_SIGNATURE;
+ header.Version = MINIDUMP_VERSION;
+ header.NumberOfStreams = 1;
+ EXPECT_TRUE(string_file.SeekSet(0));
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ ProcessSnapshotMinidump process_snapshot;
+ EXPECT_TRUE(process_snapshot.Initialize(&string_file));
+
+ UUID client_id;
+ process_snapshot.ClientID(&client_id);
+ EXPECT_EQ(UUID(), client_id);
+
+ const auto annotations_simple_map = process_snapshot.AnnotationsSimpleMap();
+ EXPECT_EQ(dictionary, annotations_simple_map);
+}
+
+TEST(ProcessSnapshotMinidump, Modules) {
+ StringFile string_file;
+
+ MINIDUMP_HEADER header = {};
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ MINIDUMP_MODULE minidump_module = {};
+ uint32_t minidump_module_count = 3;
+
+ MINIDUMP_DIRECTORY minidump_module_list_directory = {};
+ minidump_module_list_directory.StreamType = kMinidumpStreamTypeModuleList;
+ minidump_module_list_directory.Location.DataSize =
+ sizeof(MINIDUMP_MODULE_LIST) +
+ minidump_module_count * sizeof(MINIDUMP_MODULE);
+ minidump_module_list_directory.Location.Rva =
+ static_cast<RVA>(string_file.SeekGet());
+
+ EXPECT_TRUE(
+ string_file.Write(&minidump_module_count, sizeof(minidump_module_count)));
+ for (uint32_t minidump_module_index = 0;
+ minidump_module_index < minidump_module_count;
+ ++minidump_module_index) {
+ EXPECT_TRUE(string_file.Write(&minidump_module, sizeof(minidump_module)));
+ }
+
+ MinidumpModuleCrashpadInfo crashpad_module_0 = {};
+ crashpad_module_0.version = MinidumpModuleCrashpadInfo::kVersion;
+ std::map<std::string, std::string> dictionary_0;
+ dictionary_0["ptype"] = "browser";
+ dictionary_0["pid"] = "12345";
+ WriteMinidumpSimpleStringDictionary(
+ &crashpad_module_0.simple_annotations, &string_file, dictionary_0);
+
+ MinidumpModuleCrashpadInfoLink crashpad_module_0_link = {};
+ crashpad_module_0_link.minidump_module_list_index = 0;
+ crashpad_module_0_link.location.DataSize = sizeof(crashpad_module_0);
+ crashpad_module_0_link.location.Rva = static_cast<RVA>(string_file.SeekGet());
+ EXPECT_TRUE(string_file.Write(&crashpad_module_0, sizeof(crashpad_module_0)));
+
+ MinidumpModuleCrashpadInfo crashpad_module_2 = {};
+ crashpad_module_2.version = MinidumpModuleCrashpadInfo::kVersion;
+ std::map<std::string, std::string> dictionary_2;
+ dictionary_2["fakemodule"] = "yes";
+ WriteMinidumpSimpleStringDictionary(
+ &crashpad_module_2.simple_annotations, &string_file, dictionary_2);
+
+ std::vector<std::string> list_annotations_2;
+ list_annotations_2.push_back("first string");
+ list_annotations_2.push_back("last string");
+ WriteMinidumpStringList(
+ &crashpad_module_2.list_annotations, &string_file, list_annotations_2);
+
+ MinidumpModuleCrashpadInfoLink crashpad_module_2_link = {};
+ crashpad_module_2_link.minidump_module_list_index = 2;
+ crashpad_module_2_link.location.DataSize = sizeof(crashpad_module_2);
+ crashpad_module_2_link.location.Rva = static_cast<RVA>(string_file.SeekGet());
+ EXPECT_TRUE(string_file.Write(&crashpad_module_2, sizeof(crashpad_module_2)));
+
+ MinidumpCrashpadInfo crashpad_info = {};
+ crashpad_info.version = MinidumpCrashpadInfo::kVersion;
+
+ uint32_t crashpad_module_count = 2;
+
+ crashpad_info.module_list.DataSize =
+ sizeof(MinidumpModuleCrashpadInfoList) +
+ crashpad_module_count * sizeof(MinidumpModuleCrashpadInfoLink);
+ crashpad_info.module_list.Rva = static_cast<RVA>(string_file.SeekGet());
+
+ EXPECT_TRUE(
+ string_file.Write(&crashpad_module_count, sizeof(crashpad_module_count)));
+ EXPECT_TRUE(string_file.Write(&crashpad_module_0_link,
+ sizeof(crashpad_module_0_link)));
+ EXPECT_TRUE(string_file.Write(&crashpad_module_2_link,
+ sizeof(crashpad_module_2_link)));
+
+ MINIDUMP_DIRECTORY crashpad_info_directory = {};
+ crashpad_info_directory.StreamType = kMinidumpStreamTypeCrashpadInfo;
+ crashpad_info_directory.Location.DataSize = sizeof(crashpad_info);
+ crashpad_info_directory.Location.Rva =
+ static_cast<RVA>(string_file.SeekGet());
+ EXPECT_TRUE(string_file.Write(&crashpad_info, sizeof(crashpad_info)));
+
+ header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());
+ EXPECT_TRUE(string_file.Write(&minidump_module_list_directory,
+ sizeof(minidump_module_list_directory)));
+ EXPECT_TRUE(string_file.Write(&crashpad_info_directory,
+ sizeof(crashpad_info_directory)));
+
+ header.Signature = MINIDUMP_SIGNATURE;
+ header.Version = MINIDUMP_VERSION;
+ header.NumberOfStreams = 2;
+ EXPECT_TRUE(string_file.SeekSet(0));
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ ProcessSnapshotMinidump process_snapshot;
+ EXPECT_TRUE(process_snapshot.Initialize(&string_file));
+
+ std::vector<const ModuleSnapshot*> modules = process_snapshot.Modules();
+ ASSERT_EQ(minidump_module_count, modules.size());
+
+ auto annotations_simple_map = modules[0]->AnnotationsSimpleMap();
+ EXPECT_EQ(dictionary_0, annotations_simple_map);
+
+ auto annotations_vector = modules[0]->AnnotationsVector();
+ EXPECT_TRUE(annotations_vector.empty());
+
+ annotations_simple_map = modules[1]->AnnotationsSimpleMap();
+ EXPECT_TRUE(annotations_simple_map.empty());
+
+ annotations_vector = modules[1]->AnnotationsVector();
+ EXPECT_TRUE(annotations_vector.empty());
+
+ annotations_simple_map = modules[2]->AnnotationsSimpleMap();
+ EXPECT_EQ(dictionary_2, annotations_simple_map);
+
+ annotations_vector = modules[2]->AnnotationsVector();
+ EXPECT_EQ(list_annotations_2, annotations_vector);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/module_snapshot.h b/chromium/third_party/crashpad/crashpad/snapshot/module_snapshot.h
new file mode 100644
index 00000000000..d8f1ae6bd92
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/module_snapshot.h
@@ -0,0 +1,158 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MODULE_SNAPSHOT_H_
+#define CRASHPAD_SNAPSHOT_MODULE_SNAPSHOT_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "util/misc/uuid.h"
+
+namespace crashpad {
+
+//! \brief An abstract interface to a snapshot representing a code module
+//! (binary image) loaded into a snapshot process.
+class ModuleSnapshot {
+ public:
+ virtual ~ModuleSnapshot() {}
+
+ //! \brief A module’s type.
+ enum ModuleType {
+ //! \brief The module’s type is unknown.
+ kModuleTypeUnknown = 0,
+
+ //! \brief The module is a main executable.
+ kModuleTypeExecutable,
+
+ //! \brief The module is a shared library.
+ //!
+ //! \sa kModuleTypeLoadableModule
+ kModuleTypeSharedLibrary,
+
+ //! \brief The module is a loadable module.
+ //!
+ //! On some platforms, loadable modules are distinguished from shared
+ //! libraries. On these platforms, a shared library is a module that another
+ //! module links against directly, and a loadable module is not. Loadable
+ //! modules tend to be binary plug-ins.
+ kModuleTypeLoadableModule,
+
+ //! \brief The module is a dynamic loader.
+ //!
+ //! This is the module responsible for loading other modules. This is
+ //! normally `dyld` for Mac OS X and `ld.so` for Linux and other systems
+ //! using ELF.
+ kModuleTypeDynamicLoader,
+ };
+
+ //! \brief Returns the module’s pathname.
+ virtual std::string Name() const = 0;
+
+ //! \brief Returns the base address that the module is loaded at in the
+ //! snapshot process.
+ virtual uint64_t Address() const = 0;
+
+ //! \brief Returns the size that the module occupies in the snapshot process’
+ //! address space, starting at its base address.
+ //!
+ //! For Mac OS X snapshots, this method only reports the size of the `__TEXT`
+ //! segment, because segments may not be loaded contiguously.
+ virtual uint64_t Size() const = 0;
+
+ //! \brief Returns the module’s timestamp, if known.
+ //!
+ //! The timestamp is typically the modification time of the file that provided
+ //! the module in `time_t` format, seconds since the POSIX epoch. If the
+ //! module’s timestamp is unknown, this method returns `0`.
+ virtual time_t Timestamp() const = 0;
+
+ //! \brief Returns the module’s file version in the \a version_* parameters.
+ //!
+ //! If no file version can be determined, the \a version_* parameters are set
+ //! to `0`.
+ //!
+ //! For Mac OS X snapshots, this is taken from the module’s `LC_ID_DYLIB` load
+ //! command for shared libraries, and is `0` for other module types.
+ virtual void FileVersion(uint16_t* version_0,
+ uint16_t* version_1,
+ uint16_t* version_2,
+ uint16_t* version_3) const = 0;
+
+ //! \brief Returns the module’s source version in the \a version_* parameters.
+ //!
+ //! If no source version can be determined, the \a version_* parameters are
+ //! set to `0`.
+ //!
+ //! For Mac OS X snapshots, this is taken from the module’s
+ //! `LC_SOURCE_VERSION` load command.
+ virtual void SourceVersion(uint16_t* version_0,
+ uint16_t* version_1,
+ uint16_t* version_2,
+ uint16_t* version_3) const = 0;
+
+ //! \brief Returns the module’s type.
+ virtual ModuleType GetModuleType() const = 0;
+
+ //! \brief Returns the module’s UUID in the \a uuid parameter.
+ //!
+ //! A snapshot module’s UUID is taken directly from the module itself. If the
+ //! module does not have a UUID, the \a uuid parameter will be zeroed out.
+ virtual void UUID(crashpad::UUID* uuid) const = 0;
+
+ //! \brief Returns string annotations recorded in the module.
+ //!
+ //! This method retrieves annotations recorded in a module. These annotations
+ //! are intended for diagnostic use, including crash analysis. A module may
+ //! contain multiple annotations, so they are returned in a vector.
+ //!
+ //! For Mac OS X snapshots, these annotations are found by interpreting the
+ //! module’s `__DATA, __crash_info` section as `crashreporter_annotations_t`.
+ //! System libraries using the crash reporter client interface may reference
+ //! annotations in this structure. Additional annotations messages may be
+ //! found in other locations, which may be module-specific. The dynamic linker
+ //! (`dyld`) can provide an annotation at its `_error_string` symbol.
+ //!
+ //! The annotations returned by this method do not duplicate those returned by
+ //! AnnotationsSimpleMap().
+ virtual std::vector<std::string> AnnotationsVector() const = 0;
+
+ //! \brief Returns key-value string annotations recorded in the module.
+ //!
+ //! This method retrieves annotations recorded in a module. These annotations
+ //! are intended for diagnostic use, including crash analysis. “Simple
+ //! annotations” are structured as a sequence of key-value pairs, where all
+ //! keys and values are strings. These are referred to in Chrome as “crash
+ //! keys.”
+ //!
+ //! For Mac OS X snapshots, these annotations are found by interpreting the
+ //! `__DATA, __crashpad_info` section as `CrashpadInfo`. Clients can use the
+ //! Crashpad client interface to store annotations in this structure. Most
+ //! annotations under the client’s direct control will be retrievable by this
+ //! method. For clients such as Chrome, this includes the process type.
+ //!
+ //! The annotations returned by this method do not duplicate those returned by
+ //! AnnotationsVector(). Additional annotations related to the process,
+ //! system, or snapshot producer may be obtained by calling
+ //! ProcessSnapshot::AnnotationsSimpleMap().
+ virtual std::map<std::string, std::string> AnnotationsSimpleMap() const = 0;
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MODULE_SNAPSHOT_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/process_snapshot.h b/chromium/third_party/crashpad/crashpad/snapshot/process_snapshot.h
new file mode 100644
index 00000000000..42df05c9b21
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/process_snapshot.h
@@ -0,0 +1,167 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_PROCESS_SNAPSHOT_H_
+#define CRASHPAD_SNAPSHOT_PROCESS_SNAPSHOT_H_
+
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "util/misc/uuid.h"
+
+namespace crashpad {
+
+class ExceptionSnapshot;
+class ModuleSnapshot;
+class SystemSnapshot;
+class ThreadSnapshot;
+
+//! \brief An abstract interface to a snapshot representing the state of a
+//! process.
+//!
+//! This is the top-level object in a family of Snapshot objects, because it
+//! gives access to a SystemSnapshot, vectors of ModuleSnapshot and
+//! ThreadSnapshot objects, and possibly an ExceptionSnapshot. In turn,
+//! ThreadSnapshot and ExceptionSnapshot objects both give access to CPUContext
+//! objects, and ThreadSnapshot objects also give access to MemorySnapshot
+//! objects corresponding to thread stacks.
+class ProcessSnapshot {
+ public:
+ virtual ~ProcessSnapshot() {}
+
+ //! \brief Returns the snapshot process’ process ID.
+ virtual pid_t ProcessID() const = 0;
+
+ //! \brief Returns the snapshot process’ parent process’ process ID.
+ virtual pid_t ParentProcessID() const = 0;
+
+ //! \brief Returns the time that the snapshot was taken in \a snapshot_time.
+ //!
+ //! \param[out] snapshot_time The time that the snapshot was taken. This is
+ //! distinct from the time that a ProcessSnapshot object was created or
+ //! initialized, although it may be that time for ProcessSnapshot objects
+ //! representing live or recently-crashed process state.
+ virtual void SnapshotTime(timeval* snapshot_time) const = 0;
+
+ //! \brief Returns the time that the snapshot process was started in \a
+ //! start_time.
+ //!
+ //! Normally, process uptime in wall clock time can be computed as
+ //! SnapshotTime() − ProcessStartTime(), but this cannot be guaranteed in
+ //! cases where the real-time clock has been set during the snapshot process’
+ //! lifetime.
+ //!
+ //! \param[out] start_time The time that the process was started.
+ virtual void ProcessStartTime(timeval* start_time) const = 0;
+
+ //! \brief Returns the snapshot process’ CPU usage times in \a user_time and
+ //! \a system_time.
+ //!
+ //! \param[out] user_time The time that the process has spent executing in
+ //! user mode.
+ //! \param[out] system_time The time that the process has spent executing in
+ //! system (kernel) mode.
+ virtual void ProcessCPUTimes(timeval* user_time,
+ timeval* system_time) const = 0;
+
+ //! \brief Returns a %UUID identifying the event that the snapshot describes.
+ //!
+ //! This provides a stable identifier for a crash even as the report is
+ //! converted to different formats, provided that all formats support storing
+ //! a crash report ID. When a report is originally created, a report ID should
+ //! be assigned. From that point on, any operations involving the same report
+ //! should preserve the same report ID.
+ //!
+ //! If no identifier is available, this field will contain zeroes.
+ virtual void ReportID(UUID* client_id) const = 0;
+
+ //! \brief Returns a %UUID identifying the client that the snapshot
+ //! represents.
+ //!
+ //! Client identification is within the scope of the application, but it is
+ //! expected that the identifier will be unique for an instance of Crashpad
+ //! monitoring an application or set of applications for a user. The
+ //! identifier shall remain stable over time.
+ //!
+ //! If no identifier is available, this field will contain zeroes.
+ virtual void ClientID(UUID* client_id) const = 0;
+
+ //! \brief Returns key-value string annotations recorded for the process,
+ //! system, or snapshot producer.
+ //!
+ //! This method retrieves annotations recorded for a process. These
+ //! annotations are intended for diagnostic use, including crash analysis.
+ //! “Simple annotations” are structured as a sequence of key-value pairs,
+ //! where all keys and values are strings. These are referred to in Chrome as
+ //! “crash keys.”
+ //!
+ //! Annotations stored here may reflect the process, system, or snapshot
+ //! producer. Most annotations not under the client’s direct control will be
+ //! retrievable by this method. For clients such as Chrome, this includes the
+ //! product name and version.
+ //!
+ //! Additional per-module annotations may be obtained by calling
+ //! ModuleSnapshot::AnnotationsSimpleMap().
+ //
+ // This interface currently returns a const& because all implementations store
+ // the data within their objects in this format, and are therefore able to
+ // provide this access without requiring a copy. Future implementations may
+ // obtain data on demand or store data in a different format, at which point
+ // the cost of maintaining this data in ProcessSnapshot subclass objects will
+ // need to be taken into account, and this interface possibly revised.
+ virtual const std::map<std::string, std::string>& AnnotationsSimpleMap()
+ const = 0;
+
+ //! \brief Returns a SystemSnapshot reflecting the characteristics of the
+ //! system that ran the snapshot process at the time of the snapshot.
+ //!
+ //! \return A SystemSnapshot object. The caller does not take ownership of
+ //! this object, it is scoped to the lifetime of the ProcessSnapshot
+ //! object that it was obtained from.
+ virtual const SystemSnapshot* System() const = 0;
+
+ //! \brief Returns ModuleSnapshot objects reflecting the code modules (binary
+ //! images) loaded into the snapshot process at the time of the snapshot.
+ //!
+ //! \return A vector of ModuleSnapshot objects. The caller does not take
+ //! ownership of these objects, they are scoped to the lifetime of the
+ //! ProcessSnapshot object that they were obtained from.
+ virtual std::vector<const ModuleSnapshot*> Modules() const = 0;
+
+ //! \brief Returns ThreadSnapshot objects reflecting the threads (lightweight
+ //! processes) existing in the snapshot process at the time of the
+ //! snapshot.
+ //!
+ //! \return A vector of ThreadSnapshot objects. The caller does not take
+ //! ownership of these objects, they are scoped to the lifetime of the
+ //! ProcessSnapshot object that they were obtained from.
+ virtual std::vector<const ThreadSnapshot*> Threads() const = 0;
+
+ //! \brief Returns an ExceptionSnapshot reflecting the exception that the
+ //! snapshot process sustained to trigger the snapshot being taken.
+ //!
+ //! \return An ExceptionSnapshot object. The caller does not take ownership of
+ //! this object, it is scoped to the lifetime of the ProcessSnapshot
+ //! object that it was obtained from. If the snapshot is not a result of
+ //! an exception, returns `nullptr`.
+ virtual const ExceptionSnapshot* Exception() const = 0;
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_PROCESS_SNAPSHOT_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/snapshot.gyp b/chromium/third_party/crashpad/crashpad/snapshot/snapshot.gyp
new file mode 100644
index 00000000000..dcb386f6c24
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/snapshot.gyp
@@ -0,0 +1,113 @@
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'includes': [
+ '../build/crashpad.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'crashpad_snapshot',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../client/client.gyp:crashpad_client',
+ '../compat/compat.gyp:crashpad_compat',
+ '../third_party/mini_chromium/mini_chromium.gyp:base',
+ '../util/util.gyp:crashpad_util',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'cpu_architecture.h',
+ 'cpu_context.cc',
+ 'cpu_context.h',
+ 'crashpad_info_client_options.cc',
+ 'crashpad_info_client_options.h',
+ 'exception_snapshot.h',
+ 'mac/cpu_context_mac.cc',
+ 'mac/cpu_context_mac.h',
+ 'mac/exception_snapshot_mac.cc',
+ 'mac/exception_snapshot_mac.h',
+ 'mac/mach_o_image_annotations_reader.cc',
+ 'mac/mach_o_image_annotations_reader.h',
+ 'mac/mach_o_image_reader.cc',
+ 'mac/mach_o_image_reader.h',
+ 'mac/mach_o_image_segment_reader.cc',
+ 'mac/mach_o_image_segment_reader.h',
+ 'mac/mach_o_image_symbol_table_reader.cc',
+ 'mac/mach_o_image_symbol_table_reader.h',
+ 'mac/memory_snapshot_mac.cc',
+ 'mac/memory_snapshot_mac.h',
+ 'mac/module_snapshot_mac.cc',
+ 'mac/module_snapshot_mac.h',
+ 'mac/process_reader.cc',
+ 'mac/process_reader.h',
+ 'mac/process_snapshot_mac.cc',
+ 'mac/process_snapshot_mac.h',
+ 'mac/process_types.cc',
+ 'mac/process_types.h',
+ 'mac/process_types/all.proctype',
+ 'mac/process_types/crashpad_info.proctype',
+ 'mac/process_types/crashreporterclient.proctype',
+ 'mac/process_types/custom.cc',
+ 'mac/process_types/dyld_images.proctype',
+ 'mac/process_types/flavors.h',
+ 'mac/process_types/internal.h',
+ 'mac/process_types/loader.proctype',
+ 'mac/process_types/nlist.proctype',
+ 'mac/process_types/traits.h',
+ 'mac/system_snapshot_mac.cc',
+ 'mac/system_snapshot_mac.h',
+ 'mac/thread_snapshot_mac.cc',
+ 'mac/thread_snapshot_mac.h',
+ 'minidump/minidump_simple_string_dictionary_reader.cc',
+ 'minidump/minidump_simple_string_dictionary_reader.h',
+ 'minidump/minidump_string_list_reader.cc',
+ 'minidump/minidump_string_list_reader.h',
+ 'minidump/minidump_string_reader.cc',
+ 'minidump/minidump_string_reader.h',
+ 'minidump/module_snapshot_minidump.cc',
+ 'minidump/module_snapshot_minidump.h',
+ 'minidump/process_snapshot_minidump.cc',
+ 'minidump/process_snapshot_minidump.h',
+ 'memory_snapshot.h',
+ 'module_snapshot.h',
+ 'process_snapshot.h',
+ 'system_snapshot.h',
+ 'thread_snapshot.h',
+ 'win/module_snapshot_win.cc',
+ 'win/module_snapshot_win.h',
+ 'win/pe_image_reader.cc',
+ 'win/pe_image_reader.h',
+ 'win/process_reader_win.cc',
+ 'win/process_reader_win.h',
+ 'win/process_snapshot_win.cc',
+ 'win/process_snapshot_win.h',
+ 'win/system_snapshot_win.cc',
+ 'win/system_snapshot_win.h',
+ ],
+ 'conditions': [
+ ['OS=="win"', {
+ 'link_settings': {
+ 'libraries': [
+ '-lpowrprof.lib',
+ '-lversion.lib',
+ ],
+ },
+ }],
+ ]
+ },
+ ],
+}
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/snapshot_test.gyp b/chromium/third_party/crashpad/crashpad/snapshot/snapshot_test.gyp
new file mode 100644
index 00000000000..c05a7f11450
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/snapshot_test.gyp
@@ -0,0 +1,105 @@
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'includes': [
+ '../build/crashpad.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'crashpad_snapshot_test_lib',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'snapshot.gyp:crashpad_snapshot',
+ '../compat/compat.gyp:crashpad_compat',
+ '../third_party/mini_chromium/mini_chromium.gyp:base',
+ '../util/util.gyp:crashpad_util',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'test/test_cpu_context.cc',
+ 'test/test_cpu_context.h',
+ 'test/test_exception_snapshot.cc',
+ 'test/test_exception_snapshot.h',
+ 'test/test_memory_snapshot.cc',
+ 'test/test_memory_snapshot.h',
+ 'test/test_module_snapshot.cc',
+ 'test/test_module_snapshot.h',
+ 'test/test_process_snapshot.cc',
+ 'test/test_process_snapshot.h',
+ 'test/test_system_snapshot.cc',
+ 'test/test_system_snapshot.h',
+ 'test/test_thread_snapshot.cc',
+ 'test/test_thread_snapshot.h',
+ ],
+ },
+ {
+ 'target_name': 'crashpad_snapshot_test',
+ 'type': 'executable',
+ 'dependencies': [
+ 'crashpad_snapshot_test_module',
+ 'snapshot.gyp:crashpad_snapshot',
+ '../client/client.gyp:crashpad_client',
+ '../compat/compat.gyp:crashpad_compat',
+ '../test/test.gyp:crashpad_test',
+ '../third_party/gtest/gtest.gyp:gtest',
+ '../third_party/gtest/gtest.gyp:gtest_main',
+ '../third_party/mini_chromium/mini_chromium.gyp:base',
+ '../util/util.gyp:crashpad_util',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'cpu_context_test.cc',
+ 'crashpad_info_client_options_test.cc',
+ 'mac/cpu_context_mac_test.cc',
+ 'mac/mach_o_image_annotations_reader_test.cc',
+ 'mac/mach_o_image_reader_test.cc',
+ 'mac/mach_o_image_segment_reader_test.cc',
+ 'mac/process_reader_test.cc',
+ 'mac/process_types_test.cc',
+ 'mac/system_snapshot_mac_test.cc',
+ 'minidump/process_snapshot_minidump_test.cc',
+ 'win/process_reader_win_test.cc',
+ 'win/system_snapshot_win_test.cc',
+ ],
+ 'conditions': [
+ ['OS=="mac"', {
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/OpenCL.framework',
+ ],
+ },
+ }],
+ ],
+ },
+ {
+ 'target_name': 'crashpad_snapshot_test_module',
+ 'type': 'loadable_module',
+ 'dependencies': [
+ '../client/client.gyp:crashpad_client',
+ '../third_party/mini_chromium/mini_chromium.gyp:base',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'crashpad_info_client_options_test_module.cc',
+ ],
+ },
+ ],
+}
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/system_snapshot.h b/chromium/third_party/crashpad/crashpad/snapshot/system_snapshot.h
new file mode 100644
index 00000000000..82f5d4806d6
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/system_snapshot.h
@@ -0,0 +1,270 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_SYSTEM_SNAPSHOT_H_
+#define CRASHPAD_SNAPSHOT_SYSTEM_SNAPSHOT_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include "snapshot/cpu_architecture.h"
+
+namespace crashpad {
+
+//! \brief An abstract interface to a snapshot representing the state of a
+//! system, comprising an operating system, CPU architecture, and various
+//! other characteristics.
+class SystemSnapshot {
+ public:
+ virtual ~SystemSnapshot() {}
+
+ //! \brief A system’s operating system family.
+ enum OperatingSystem {
+ //! \brief The snapshot system’s operating system is unknown.
+ kOperatingSystemUnknown = 0,
+
+ //! \brief Mac OS X.
+ kOperatingSystemMacOSX,
+
+ //! \brief Windows.
+ kOperatingSystemWindows,
+ };
+
+ //! \brief A system’s daylight saving time status.
+ //!
+ //! The daylight saving time status is taken partially from the system’s
+ //! locale configuration. This determines whether daylight saving time is
+ //! ever observed on the system. If it is, the snapshot’s time
+ //! (ProcessSnapshot::SnapshotTime()) is used to determine whether the system
+ //! was observing daylight saving time at the time of the snapshot.
+ enum DaylightSavingTimeStatus {
+ //! \brief Daylight saving time is never observed on the snapshot system.
+ kDoesNotObserveDaylightSavingTime = 0,
+
+ //! \brief Daylight saving time is observed on the snapshot system when in
+ //! effect, but standard time was in effect at the time of the snapshot.
+ kObservingStandardTime,
+
+ //! \brief Daylight saving time is observed on the snapshot system when in
+ //! effect, and daylight saving time was in effect at the time of the
+ //! snapshot.
+ kObservingDaylightSavingTime,
+ };
+
+ //! \brief Returns the snapshot system’s CPU architecture.
+ //!
+ //! In some cases, a system may be able to run processes of multiple specific
+ //! architecture types. For example, systems based on 64-bit architectures
+ //! such as x86_64 are often able to run 32-bit code of another architecture
+ //! in the same family, such as 32-bit x86. On these systems, this method will
+ //! return the architecture of the process that the snapshot is associated
+ //! with, provided that the SystemSnapshot object was obtained from
+ //! ProcessSnapshot::System(). This renders one aspect of this method’s return
+ //! value a process attribute rather than a system attribute, but it’s defined
+ //! here rather than in ProcessSnapshot because the CPU architecture is a
+ //! better conceptual fit for the system abstraction alongside these other
+ //! related methods.
+ virtual CPUArchitecture GetCPUArchitecture() const = 0;
+
+ //! \brief Returns the snapshot system’s CPU revision.
+ //!
+ //! For x86-family CPUs (including x86_64 and 32-bit x86), this is the CPU
+ //! family, model, and stepping ID values from `cpuid 1` `eax`. The family and
+ //! model values are adjusted to take the extended family and model IDs into
+ //! account. These values are encoded in this method’s return value with the
+ //! family in the high high 16 bits, the model in the next 8 bits, and the
+ //! stepping in the low 8 bits.
+ //!
+ //! \return A CPU architecture-specific value identifying the CPU revision.
+ virtual uint32_t CPURevision() const = 0;
+
+ //! \brief Returns the total number of CPUs present in the snapshot system.
+ virtual uint8_t CPUCount() const = 0;
+
+ //! \brief Returns the vendor of the snapshot system’s CPUs.
+ //!
+ //! For x86-family CPUs (including x86_64 and 32-bit x86), this is the CPU
+ //! vendor identification string as encoded in `cpuid 0` `ebx`, `edx`, and
+ //! `ecx`.
+ //!
+ //! \return A string identifying the vendor of the snapshot system’s CPUs.
+ virtual std::string CPUVendor() const = 0;
+
+ //! \brief Returns frequency information about the snapshot system’s CPUs in
+ //! \current_hz and \a max_hz.
+ //!
+ //! \param[out] current_hz The snapshot system’s CPU clock frequency in Hz at
+ //! the time of the snapshot.
+ //! \param[out] max_hz The snapshot system’s maximum possible CPU clock
+ //! frequency.
+ virtual void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const = 0;
+
+ //! \brief Returns an x86-family snapshot system’s CPU signature.
+ //!
+ //! This is the family, model, and stepping ID values as encoded in `cpuid 1`
+ //! `eax`.
+ //!
+ //! This method must only be called when GetCPUArchitecture() indicates an
+ //! x86-family CPU architecture (#kCPUArchitectureX86 or
+ //! #kCPUArchitectureX86_64).
+ //!
+ //! \return An x86 family-specific value identifying the CPU signature.
+ virtual uint32_t CPUX86Signature() const = 0;
+
+ //! \brief Returns an x86-family snapshot system’s CPU features.
+ //!
+ //! This is the feature information as encoded in `cpuid 1` `edx` and `ecx`.
+ //! `edx` is placed in the low half of the return value, and `ecx` is placed
+ //! in the high half.
+ //!
+ //! This method must only be called when GetCPUArchitecture() indicates an
+ //! x86-family CPU architecture (#kCPUArchitectureX86 or
+ //! #kCPUArchitectureX86_64).
+ //!
+ //! \return An x86 family-specific value identifying CPU features.
+ //!
+ //! \sa CPUX86ExtendedFeatures()
+ //! \sa CPUX86Leaf7Features()
+ virtual uint64_t CPUX86Features() const = 0;
+
+ //! \brief Returns an x86-family snapshot system’s extended CPU features.
+ //!
+ //! This is the extended feature information as encoded in `cpuid 0x80000001`
+ //! `edx` and `ecx`. `edx` is placed in the low half of the return value, and
+ //! `ecx` is placed in the high half.
+ //!
+ //! This method must only be called when GetCPUArchitecture() indicates an
+ //! x86-family CPU architecture (#kCPUArchitectureX86 or
+ //! #kCPUArchitectureX86_64).
+ //!
+ //! \return An x86 family-specific value identifying extended CPU features.
+ //!
+ //! \sa CPUX86Features()
+ //! \sa CPUX86Leaf7Features()
+ virtual uint64_t CPUX86ExtendedFeatures() const = 0;
+
+ //! \brief Returns an x86-family snapshot system’s “leaf 7” CPU features.
+ //!
+ //! This is the “leaf 7” feature information as encoded in `cpuid 7` `ebx`. If
+ //! `cpuid 7` is not supported by the snapshot CPU, this returns `0`.
+ //!
+ //! This method must only be called when GetCPUArchitecture() indicates an
+ //! x86-family CPU architecture (#kCPUArchitectureX86 or
+ //! #kCPUArchitectureX86_64).
+ //!
+ //! \return An x86 family-specific value identifying “leaf 7” CPU features.
+ //!
+ //! \sa CPUX86Features()
+ //! \sa CPUX86ExtendedFeatures()
+ virtual uint32_t CPUX86Leaf7Features() const = 0;
+
+ //! \brief Returns an x86-family snapshot system’s CPU’s support for the SSE
+ //! DAZ (“denormals are zeros”) mode.
+ //!
+ //! This determines whether the CPU supports DAZ mode at all, not whether this
+ //! mode is enabled for any particular thread. DAZ mode support is detected by
+ //! examining the DAZ bit in the `mxcsr_mask` field of the floating-point
+ //! context saved by `fxsave`.
+ //!
+ //! This method must only be called when GetCPUArchitecture() indicates an
+ //! x86-family CPU architecture (#kCPUArchitectureX86 or
+ //! #kCPUArchitectureX86_64).
+ //!
+ //! \return `true` if the snapshot system’s CPUs support the SSE DAZ mode,
+ //! `false` if they do not.
+ virtual bool CPUX86SupportsDAZ() const = 0;
+
+ //! \brief Returns the snapshot system’s operating system family.
+ virtual OperatingSystem GetOperatingSystem() const = 0;
+
+ //! \brief Returns whether the snapshot system runs a server variant of its
+ //! operating system.
+ virtual bool OSServer() const = 0;
+
+ //! \brief Returns the snapshot system’s operating system version information
+ //! in \a major, \a minor, \a bugfix, and \a build.
+ //!
+ //! \param[out] major The snapshot system’s operating system’s first (major)
+ //! version number component. This would be `10` for Mac OS X 10.9.5, and
+ //! `6` for Windows 7 (NT 6.1) SP1 version 6.1.7601.
+ //! \param[out] minor The snapshot system’s operating system’s second (minor)
+ //! version number component. This would be `9` for Mac OS X 10.9.5, and
+ //! `1` for Windows 7 (NT 6.1) SP1 version 6.1.7601.
+ //! \param[out] bugfix The snapshot system’s operating system’s third (bugfix)
+ //! version number component. This would be `5` for Mac OS X 10.9.5, and
+ //! `7601` for Windows 7 (NT 6.1) SP1 version 6.1.7601.
+ //! \param[out] build A string further identifying an operating system
+ //! version. For Mac OS X 10.9.5, this would be `"13F34"`. For Windows,
+ //! this would be `"Service Pack 1"` if that service pack was installed.
+ //! For Linux and other Unix-like systems, this would be the kernel
+ //! version from `uname -srvm`, possibly with additional information
+ //! appended. On Android, the `ro.build.fingerprint` system property would
+ //! be appended.
+ virtual void OSVersion(int* major,
+ int* minor,
+ int* bugfix,
+ std::string* build) const = 0;
+
+ //! \brief Returns the snapshot system’s full operating system version
+ //! information in string format.
+ //!
+ //! For Mac OS X, the string contains values from the operating system and
+ //! kernel. A Mac OS X 10.9.5 snapshot system would be identified as `"Mac OS
+ //! X 10.9.5 (13F34); Darwin 13.4.0 Darwin Kernel Version 13.4.0: Sun Aug 17
+ //! 19:50:11 PDT 2014; root:xnu-2422.115.4~1/RELEASE_X86_64 x86_64"`.
+ virtual std::string OSVersionFull() const = 0;
+
+ //! \brief Returns a description of the snapshot system’s hardware in string
+ //! format.
+ //!
+ //! For Mac OS X, the string contains the Mac model and board ID. A mid-2014
+ //! 15" MacBook Pro would be identified as `"MacBookPro11,3
+ //! (Mac-2BD1B31983FE1663)"`.
+ virtual std::string MachineDescription() const = 0;
+
+ //! \brief Returns the status of the NX (no-execute, or XD, execute-disable)
+ //! feature on the snapshot system.
+ //!
+ //! This refers to a feature that allows mapped readable pages to be marked
+ //! as non-executable.
+ //!
+ //! \return `true` if the snapshot system supports NX and it is enabled.
+ virtual bool NXEnabled() const = 0;
+
+ //! \brief Returns time zone information from the snapshot system, based on
+ //! its locale configuration and real-time clock.
+ //!
+ //! \param[out] dst_status Whether the location observes daylight saving time,
+ //! and if so, whether it or standard time is currently being observed.
+ //! \param[out] standard_offset_seconds The number of seconds that the
+ //! location’s time zone is east (ahead) of UTC during standard time.
+ //! \param[out] daylight_offset_seconds The number of seconds that the
+ //! location’s time zone is east (ahead) of UTC during daylight saving.
+ //! time.
+ //! \param[out] standard_name The name of the time zone while standard time is
+ //! being observed.
+ //! \param[out] daylight_name The name of the time zone while daylight saving
+ //! time is being observed.
+ virtual void TimeZone(DaylightSavingTimeStatus* observes_daylight,
+ int* standard_offset_seconds,
+ int* daylight_offset_seconds,
+ std::string* standard_name,
+ std::string* daylight_name) const = 0;
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_SYSTEM_SNAPSHOT_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/thread_snapshot.h b/chromium/third_party/crashpad/crashpad/snapshot/thread_snapshot.h
new file mode 100644
index 00000000000..d43fc6e5c8e
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/thread_snapshot.h
@@ -0,0 +1,69 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_THREAD_SNAPSHOT_H_
+#define CRASHPAD_SNAPSHOT_THREAD_SNAPSHOT_H_
+
+#include <stdint.h>
+
+namespace crashpad {
+
+struct CPUContext;
+class MemorySnapshot;
+
+//! \brief An abstract interface to a snapshot representing a thread
+//! (lightweight process) present in a snapshot process.
+class ThreadSnapshot {
+ public:
+ virtual ~ThreadSnapshot() {}
+
+ //! \brief Returns a CPUContext object corresponding to the thread’s CPU
+ //! context.
+ //!
+ //! The caller does not take ownership of this object, it is scoped to the
+ //! lifetime of the ThreadSnapshot object that it was obtained from.
+ virtual const CPUContext* Context() const = 0;
+
+ //! \brief Returns a MemorySnapshot object corresponding to the memory region
+ //! that contains the thread’s stack, or `nullptr` if no stack region is
+ //! available.
+ //!
+ //! The caller does not take ownership of this object, it is scoped to the
+ //! lifetime of the ThreadSnapshot object that it was obtained from.
+ virtual const MemorySnapshot* Stack() const = 0;
+
+ //! \brief Returns the thread’s identifier.
+ //!
+ //! %Thread identifiers are at least unique within a process, and may be
+ //! unique system-wide.
+ virtual uint64_t ThreadID() const = 0;
+
+ //! \brief Returns the thread’s suspend count.
+ //!
+ //! A suspend count of `0` denotes a schedulable (not suspended) thread.
+ virtual int SuspendCount() const = 0;
+
+ //! \brief Returns the thread’s priority.
+ //!
+ //! Threads with higher priorities will have higher priority values.
+ virtual int Priority() const = 0;
+
+ //! \brief Returns the base address of a region used to store thread-specific
+ //! data.
+ virtual uint64_t ThreadSpecificDataAddress() const = 0;
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_THREAD_SNAPSHOT_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/win/module_snapshot_win.cc b/chromium/third_party/crashpad/crashpad/snapshot/win/module_snapshot_win.cc
new file mode 100644
index 00000000000..c3f915f8b4b
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/win/module_snapshot_win.cc
@@ -0,0 +1,136 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/win/module_snapshot_win.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "snapshot/win/pe_image_reader.h"
+#include "util/misc/tri_state.h"
+#include "util/misc/uuid.h"
+
+namespace crashpad {
+namespace internal {
+
+ModuleSnapshotWin::ModuleSnapshotWin()
+ : ModuleSnapshot(),
+ name_(),
+ timestamp_(0),
+ process_reader_(nullptr),
+ initialized_() {
+}
+
+ModuleSnapshotWin::~ModuleSnapshotWin() {
+}
+
+bool ModuleSnapshotWin::Initialize(
+ ProcessReaderWin* process_reader,
+ const ProcessInfo::Module& process_reader_module) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ process_reader_ = process_reader;
+ name_ = base::UTF16ToUTF8(process_reader_module.name);
+ timestamp_ = process_reader_module.timestamp;
+ pe_image_reader_.reset(new PEImageReader());
+ if (!pe_image_reader_->Initialize(process_reader_,
+ process_reader_module.dll_base,
+ process_reader_module.size,
+ name_)) {
+ return false;
+ }
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+void ModuleSnapshotWin::GetCrashpadOptions(CrashpadInfoClientOptions* options) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ process_types::CrashpadInfo crashpad_info;
+ if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info)) {
+ options->crashpad_handler_behavior = TriState::kUnset;
+ options->system_crash_reporter_forwarding = TriState::kUnset;
+ return;
+ }
+
+ options->crashpad_handler_behavior =
+ CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
+ crashpad_info.crashpad_handler_behavior);
+
+ options->system_crash_reporter_forwarding =
+ CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
+ crashpad_info.system_crash_reporter_forwarding);
+}
+
+std::string ModuleSnapshotWin::Name() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return name_;
+}
+
+uint64_t ModuleSnapshotWin::Address() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return pe_image_reader_->Address();
+}
+
+uint64_t ModuleSnapshotWin::Size() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return pe_image_reader_->Size();
+}
+
+time_t ModuleSnapshotWin::Timestamp() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return timestamp_;
+}
+
+void ModuleSnapshotWin::FileVersion(uint16_t* version_0,
+ uint16_t* version_1,
+ uint16_t* version_2,
+ uint16_t* version_3) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ CHECK(false) << "TODO(scottmg)";
+}
+
+void ModuleSnapshotWin::SourceVersion(uint16_t* version_0,
+ uint16_t* version_1,
+ uint16_t* version_2,
+ uint16_t* version_3) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ CHECK(false) << "TODO(scottmg)";
+}
+
+ModuleSnapshot::ModuleType ModuleSnapshotWin::GetModuleType() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ CHECK(false) << "TODO(scottmg)";
+ return ModuleSnapshot::ModuleType();
+}
+
+void ModuleSnapshotWin::UUID(crashpad::UUID* uuid) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ CHECK(false) << "TODO(scottmg)";
+}
+
+std::vector<std::string> ModuleSnapshotWin::AnnotationsVector() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ CHECK(false) << "TODO(scottmg)";
+ return std::vector<std::string>();
+}
+
+std::map<std::string, std::string> ModuleSnapshotWin::AnnotationsSimpleMap()
+ const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ CHECK(false) << "TODO(scottmg)";
+ return std::map<std::string, std::string>();
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/win/module_snapshot_win.h b/chromium/third_party/crashpad/crashpad/snapshot/win/module_snapshot_win.h
new file mode 100644
index 00000000000..a25659d136d
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/win/module_snapshot_win.h
@@ -0,0 +1,96 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_WIN_MODULE_SNAPSHOT_WIN_H_
+#define CRASHPAD_SNAPSHOT_WIN_MODULE_SNAPSHOT_WIN_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "snapshot/crashpad_info_client_options.h"
+#include "snapshot/module_snapshot.h"
+#include "snapshot/win/process_reader_win.h"
+#include "util/misc/initialization_state_dcheck.h"
+#include "util/win/process_info.h"
+
+namespace crashpad {
+
+class PEImageReader;
+struct UUID;
+
+namespace internal {
+
+//! \brief A ModuleSnapshot of a code module (binary image) loaded into a
+//! running (or crashed) process on a Windows system.
+class ModuleSnapshotWin final : public ModuleSnapshot {
+ public:
+ ModuleSnapshotWin();
+ ~ModuleSnapshotWin() override;
+
+ //! \brief Initializes the object.
+ //!
+ //! \param[in] process_reader A ProcessReader for the task containing the
+ //! module.
+ //! \param[in] process_reader_module The module within the ProcessReader for
+ //! which the snapshot should be created.
+ //!
+ //! \return `true` if the snapshot could be created, `false` otherwise with
+ //! an appropriate message logged.
+ bool Initialize(ProcessReaderWin* process_reader,
+ const ProcessInfo::Module& process_reader_module);
+
+ //! \brief Returns options from the module's CrashpadInfo structure.
+ //!
+ //! \param[out] options Options set in the module's CrashpadInfo structure.
+ void GetCrashpadOptions(CrashpadInfoClientOptions* options);
+
+ // ModuleSnapshot:
+
+ std::string Name() const override;
+ uint64_t Address() const override;
+ uint64_t Size() const override;
+ time_t Timestamp() const override;
+ void FileVersion(uint16_t* version_0,
+ uint16_t* version_1,
+ uint16_t* version_2,
+ uint16_t* version_3) const override;
+ void SourceVersion(uint16_t* version_0,
+ uint16_t* version_1,
+ uint16_t* version_2,
+ uint16_t* version_3) const override;
+ ModuleType GetModuleType() const override;
+ void UUID(crashpad::UUID* uuid) const override;
+ std::vector<std::string> AnnotationsVector() const override;
+ std::map<std::string, std::string> AnnotationsSimpleMap() const override;
+
+ private:
+ std::string name_;
+ time_t timestamp_;
+ scoped_ptr<PEImageReader> pe_image_reader_;
+ ProcessReaderWin* process_reader_; // weak
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotWin);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_WIN_MODULE_SNAPSHOT_WIN_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.cc b/chromium/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.cc
new file mode 100644
index 00000000000..01755612a40
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.cc
@@ -0,0 +1,182 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/win/pe_image_reader.h"
+
+#include <string.h>
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "client/crashpad_info.h"
+#include "snapshot/win/process_reader_win.h"
+
+namespace crashpad {
+
+namespace {
+
+std::string RangeToString(const CheckedWinAddressRange& range) {
+ return base::StringPrintf("[0x%llx + 0x%llx (%s)]",
+ range.Base(),
+ range.Size(),
+ range.Is64Bit() ? "64" : "32");
+}
+
+} // namespace
+
+PEImageReader::PEImageReader()
+ : process_reader_(nullptr),
+ module_range_(),
+ module_name_(),
+ initialized_() {
+}
+
+PEImageReader::~PEImageReader() {
+}
+
+bool PEImageReader::Initialize(ProcessReaderWin* process_reader,
+ WinVMAddress address,
+ WinVMSize size,
+ const std::string& module_name) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ process_reader_ = process_reader;
+ module_range_.SetRange(process_reader_->Is64Bit(), address, size);
+ if (!module_range_.IsValid()) {
+ LOG(WARNING) << "invalid module range for " << module_name << ": "
+ << RangeToString(module_range_);
+ return false;
+ }
+ module_name_ = module_name;
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+bool PEImageReader::GetCrashpadInfo(
+ process_types::CrashpadInfo* crashpad_info) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ IMAGE_SECTION_HEADER section;
+ if (!GetSectionByName("CPADinfo", &section))
+ return false;
+
+ if (section.Misc.VirtualSize < sizeof(process_types::CrashpadInfo)) {
+ LOG(WARNING) << "small crashpad info section size "
+ << section.Misc.VirtualSize << ", " << module_name_;
+ return false;
+ }
+
+ WinVMAddress crashpad_info_address = Address() + section.VirtualAddress;
+ CheckedWinAddressRange crashpad_info_range(process_reader_->Is64Bit(),
+ crashpad_info_address,
+ section.Misc.VirtualSize);
+ if (!crashpad_info_range.IsValid()) {
+ LOG(WARNING) << "invalid range for crashpad info: "
+ << RangeToString(crashpad_info_range);
+ return false;
+ }
+
+ if (!module_range_.ContainsRange(crashpad_info_range)) {
+ LOG(WARNING) << "crashpad info does not fall inside module "
+ << module_name_;
+ return false;
+ }
+
+ // TODO(scottmg): process_types for cross-bitness.
+ if (!process_reader_->ReadMemory(crashpad_info_address,
+ sizeof(process_types::CrashpadInfo),
+ crashpad_info)) {
+ LOG(WARNING) << "could not read crashpad info " << module_name_;
+ return false;
+ }
+
+ if (crashpad_info->signature != CrashpadInfo::kSignature ||
+ crashpad_info->version < 1) {
+ LOG(WARNING) << "unexpected crashpad info data " << module_name_;
+ return false;
+ }
+
+ return true;
+}
+
+bool PEImageReader::GetSectionByName(const std::string& name,
+ IMAGE_SECTION_HEADER* section) const {
+ if (name.size() > sizeof(section->Name)) {
+ LOG(WARNING) << "supplied section name too long " << name;
+ return false;
+ }
+
+ IMAGE_DOS_HEADER dos_header;
+ if (!CheckedReadMemory(Address(), sizeof(IMAGE_DOS_HEADER), &dos_header)) {
+ LOG(WARNING) << "could not read dos header of " << module_name_;
+ return false;
+ }
+
+ if (dos_header.e_magic != IMAGE_DOS_SIGNATURE) {
+ LOG(WARNING) << "invalid e_magic in dos header of " << module_name_;
+ return false;
+ }
+
+ // TODO(scottmg): This is reading a same-bitness sized structure.
+ IMAGE_NT_HEADERS nt_headers;
+ WinVMAddress nt_headers_address = Address() + dos_header.e_lfanew;
+ if (!CheckedReadMemory(
+ nt_headers_address, sizeof(IMAGE_NT_HEADERS), &nt_headers)) {
+ LOG(WARNING) << "could not read nt headers of " << module_name_;
+ return false;
+ }
+
+ if (nt_headers.Signature != IMAGE_NT_SIGNATURE) {
+ LOG(WARNING) << "invalid signature in nt headers of " << module_name_;
+ return false;
+ }
+
+ WinVMAddress first_section_address =
+ nt_headers_address + offsetof(IMAGE_NT_HEADERS, OptionalHeader) +
+ nt_headers.FileHeader.SizeOfOptionalHeader;
+ for (DWORD i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i) {
+ WinVMAddress section_address =
+ first_section_address + sizeof(IMAGE_SECTION_HEADER) * i;
+ if (!CheckedReadMemory(
+ section_address, sizeof(IMAGE_SECTION_HEADER), section)) {
+ LOG(WARNING) << "could not read section " << i << " of " << module_name_;
+ return false;
+ }
+ if (strncmp(reinterpret_cast<const char*>(section->Name),
+ name.c_str(),
+ sizeof(section->Name)) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool PEImageReader::CheckedReadMemory(WinVMAddress address,
+ WinVMSize size,
+ void* into) const {
+ CheckedWinAddressRange read_range(process_reader_->Is64Bit(), address, size);
+ if (!read_range.IsValid()) {
+ LOG(WARNING) << "invalid read range: " << RangeToString(read_range);
+ return false;
+ }
+ if (!module_range_.ContainsRange(read_range)) {
+ LOG(WARNING) << "attempt to read outside of module " << module_name_
+ << " at range: " << RangeToString(read_range);
+ return false;
+ }
+ return process_reader_->ReadMemory(address, size, into);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.h
new file mode 100644
index 00000000000..71b4863e8ff
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.h
@@ -0,0 +1,120 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_READER_H_
+#define CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_READER_H_
+
+#include <windows.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "util/misc/initialization_state_dcheck.h"
+#include "util/win/address_types.h"
+#include "util/win/checked_win_address_range.h"
+
+namespace crashpad {
+
+class ProcessReaderWin;
+
+namespace process_types {
+
+// TODO(scottmg): Genericize and/or? move process_types out of mac/.
+struct CrashpadInfo {
+ uint32_t signature;
+ uint32_t size;
+ uint32_t version;
+ uint8_t crashpad_handler_behavior; // TriState.
+ uint8_t system_crash_reporter_forwarding; // TriState.
+ uint16_t padding_0;
+ uint64_t simple_annotations; // TODO(scottmg): x86/64.
+};
+
+struct Section {
+};
+
+} // namespace process_types
+
+//! \brief A reader for PE images mapped into another process.
+//!
+//! This class is capable of reading both 32-bit and 64-bit images based on the
+//! bitness of the remote process.
+//!
+//! \sa PEImageAnnotationsReader
+class PEImageReader {
+ public:
+ PEImageReader();
+ ~PEImageReader();
+
+ //! \brief Initializes the reader.
+ //!
+ //! This method must be called only once on an object. This method must be
+ //! called successfully before any other method in this class may be called.
+ //!
+ //! \param[in] process_reader The reader for the remote process.
+ //! \param[in] address The address, in the remote process' address space,
+ //! where the `IMAGE_DOS_HEADER` is located.
+ //! \param[in] size The size of the image.
+ //! \param[in] name The module's name, a string to be used in logged messages.
+ //! This string is for diagnostic purposes.
+ //!
+ //! \return `true` if the image was read successfully, `false` otherwise, with
+ //! an appropriate message logged.
+ bool Initialize(ProcessReaderWin* process_reader,
+ WinVMAddress address,
+ WinVMSize size,
+ const std::string& module_name);
+
+ //! \brief Returns the image's load address.
+ //!
+ //! This is the value passed as \a address to Initialize().
+ WinVMAddress Address() const { return module_range_.Base(); }
+
+ //! \brief Returns the image's size.
+ //!
+ //! This is the value passed as \a size to Initialize().
+ WinVMSize Size() const { return module_range_.Size(); }
+
+ //! \brief Obtains the module's CrashpadInfo structure.
+ //!
+ //! \return `true` on success, `false` on failure. If the module does not have
+ //! a `CPADinfo` section, this will return `false` without logging any
+ //! messages. Other failures will result in messages being logged.
+ bool GetCrashpadInfo(process_types::CrashpadInfo* crashpad_info) const;
+
+ private:
+ //! \brief Finds a given section by name in the image.
+ bool GetSectionByName(const std::string& name,
+ IMAGE_SECTION_HEADER* section) const;
+
+ //! \brief Reads memory from target process, first checking whether the range
+ //! requested falls inside module_range_.
+ //!
+ //! \return `true` on success, with \a into filled out, otherwise `false` and
+ //! a message will be logged.
+ bool CheckedReadMemory(WinVMAddress address,
+ WinVMSize size,
+ void* into) const;
+
+ ProcessReaderWin* process_reader_; // weak
+ CheckedWinAddressRange module_range_;
+ std::string module_name_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(PEImageReader);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_READER_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/win/process_reader_win.cc b/chromium/third_party/crashpad/crashpad/snapshot/win/process_reader_win.cc
new file mode 100644
index 00000000000..84e176b8739
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/win/process_reader_win.cc
@@ -0,0 +1,90 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/win/process_reader_win.h"
+
+#include "base/numerics/safe_conversions.h"
+#include "util/win/time.h"
+
+namespace crashpad {
+
+ProcessReaderWin::ProcessReaderWin()
+ : process_(INVALID_HANDLE_VALUE),
+ process_info_(),
+ modules_(),
+ initialized_() {
+}
+
+ProcessReaderWin::~ProcessReaderWin() {
+}
+
+bool ProcessReaderWin::Initialize(HANDLE process) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ process_ = process;
+ process_info_.Initialize(process);
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+bool ProcessReaderWin::ReadMemory(WinVMAddress at,
+ WinVMSize num_bytes,
+ void* into) {
+ SIZE_T bytes_read;
+ if (!ReadProcessMemory(process_,
+ reinterpret_cast<void*>(at),
+ into,
+ base::checked_cast<SIZE_T>(num_bytes),
+ &bytes_read) ||
+ num_bytes != bytes_read) {
+ PLOG(ERROR) << "ReadMemory at " << at << " of " << num_bytes << " failed";
+ return false;
+ }
+ return true;
+}
+
+bool ProcessReaderWin::StartTime(timeval* start_time) const {
+ FILETIME creation, exit, kernel, user;
+ if (!GetProcessTimes(process_, &creation, &exit, &kernel, &user)) {
+ PLOG(ERROR) << "GetProcessTimes";
+ return false;
+ }
+ *start_time = FiletimeToTimevalEpoch(creation);
+ return true;
+}
+
+bool ProcessReaderWin::CPUTimes(timeval* user_time,
+ timeval* system_time) const {
+ FILETIME creation, exit, kernel, user;
+ if (!GetProcessTimes(process_, &creation, &exit, &kernel, &user)) {
+ PLOG(ERROR) << "GetProcessTimes";
+ return false;
+ }
+ *user_time = FiletimeToTimevalInterval(user);
+ *system_time = FiletimeToTimevalInterval(kernel);
+ return true;
+}
+
+const std::vector<ProcessInfo::Module>& ProcessReaderWin::Modules() {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ if (!process_info_.Modules(&modules_)) {
+ LOG(ERROR) << "couldn't retrieve modules";
+ }
+
+ return modules_;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/win/process_reader_win.h b/chromium/third_party/crashpad/crashpad/snapshot/win/process_reader_win.h
new file mode 100644
index 00000000000..ff687885b54
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/win/process_reader_win.h
@@ -0,0 +1,84 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_WIN_PROCESS_READER_WIN_H_
+#define CRASHPAD_SNAPSHOT_WIN_PROCESS_READER_WIN_H_
+
+#include <sys/time.h>
+#include <windows.h>
+
+#include "util/misc/initialization_state_dcheck.h"
+#include "util/win/address_types.h"
+#include "util/win/process_info.h"
+
+namespace crashpad {
+
+//! \brief Accesses information about another process, identified by a HANDLE.
+class ProcessReaderWin {
+ public:
+ ProcessReaderWin();
+ ~ProcessReaderWin();
+
+ //! \brief Initializes this object. This method must be called before any
+ //! other.
+ //!
+ //! \param[in] process Process handle, must have PROCESS_QUERY_INFORMATION,
+ //! PROCESS_VM_READ, and PROCESS_DUP_HANDLE access.
+ //!
+ //! \return `true` on success, indicating that this object will respond
+ //! validly to further method calls. `false` on failure. On failure, no
+ //! further method calls should be made.
+ bool Initialize(HANDLE process);
+
+ //! \return `true` if the target task is a 64-bit process.
+ bool Is64Bit() const { return process_info_.Is64Bit(); }
+
+ pid_t ProcessID() const { return process_info_.ProcessID(); }
+ pid_t ParentProcessID() const { return process_info_.ParentProcessID(); }
+
+ bool ReadMemory(WinVMAddress at, WinVMSize num_bytes, void* into);
+
+ //! \brief Determines the target process' start time.
+ //!
+ //! \param[out] start_time The time that the process started.
+ //!
+ //! \return `true` on success, `false` on failure, with a warning logged.
+ bool StartTime(timeval* start_time) const;
+
+ //! \brief Determines the target process' execution time.
+ //!
+ //! \param[out] user_time The amount of time the process has executed code in
+ //! user mode.
+ //! \param[out] system_time The amount of time the process has executed code
+ //! in kernel mode.
+ //!
+ //! \return `true` on success, `false` on failure, with a warning logged.
+ bool CPUTimes(timeval* user_time, timeval* system_time) const;
+
+ //! \return The modules loaded in the process. The first element (at index
+ //! `0`) corresponds to the main executable.
+ const std::vector<ProcessInfo::Module>& Modules();
+
+ private:
+ HANDLE process_;
+ ProcessInfo process_info_;
+ std::vector<ProcessInfo::Module> modules_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessReaderWin);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_WIN_PROCESS_READER_WIN_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/win/process_reader_win_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/win/process_reader_win_test.cc
new file mode 100644
index 00000000000..e7c478ea5b3
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/win/process_reader_win_test.cc
@@ -0,0 +1,48 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/win/process_reader_win.h"
+
+#include <windows.h>
+
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(ProcessReaderWin, SelfBasic) {
+ ProcessReaderWin process_reader;
+ ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess()));
+
+#if !defined(ARCH_CPU_64_BITS)
+ EXPECT_FALSE(process_reader.Is64Bit());
+#else
+ EXPECT_TRUE(process_reader.Is64Bit());
+#endif
+
+ EXPECT_EQ(GetCurrentProcessId(), process_reader.ProcessID());
+
+ const char kTestMemory[] = "Some test memory";
+ char buffer[arraysize(kTestMemory)];
+ ASSERT_TRUE(
+ process_reader.ReadMemory(reinterpret_cast<uintptr_t>(kTestMemory),
+ sizeof(kTestMemory),
+ &buffer));
+ EXPECT_STREQ(kTestMemory, buffer);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.cc b/chromium/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.cc
new file mode 100644
index 00000000000..16623069ce9
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.cc
@@ -0,0 +1,164 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/win/process_snapshot_win.h"
+
+#include "snapshot/win/module_snapshot_win.h"
+#include "util/win/time.h"
+
+namespace crashpad {
+
+ProcessSnapshotWin::ProcessSnapshotWin()
+ : ProcessSnapshot(),
+ system_(),
+ // TODO(scottmg): threads_(),
+ modules_(),
+ // TODO(scottmg): exception_(),
+ process_reader_(),
+ report_id_(),
+ client_id_(),
+ annotations_simple_map_(),
+ snapshot_time_(),
+ initialized_() {
+}
+
+ProcessSnapshotWin::~ProcessSnapshotWin() {
+}
+
+bool ProcessSnapshotWin::Initialize(HANDLE process) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ GetTimeOfDay(&snapshot_time_);
+
+ if (!process_reader_.Initialize(process))
+ return false;
+
+ system_.Initialize(&process_reader_);
+
+ // TODO(scottmg): InitializeThreads();
+ InitializeModules();
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+void ProcessSnapshotWin::GetCrashpadOptions(
+ CrashpadInfoClientOptions* options) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ CrashpadInfoClientOptions local_options;
+
+ for (internal::ModuleSnapshotWin* module : modules_) {
+ CrashpadInfoClientOptions module_options;
+ module->GetCrashpadOptions(&module_options);
+
+ if (local_options.crashpad_handler_behavior == TriState::kUnset) {
+ local_options.crashpad_handler_behavior =
+ module_options.crashpad_handler_behavior;
+ }
+ if (local_options.system_crash_reporter_forwarding == TriState::kUnset) {
+ local_options.system_crash_reporter_forwarding =
+ module_options.system_crash_reporter_forwarding;
+ }
+
+ // If non-default values have been found for all options, the loop can end
+ // early.
+ if (local_options.crashpad_handler_behavior != TriState::kUnset &&
+ local_options.system_crash_reporter_forwarding != TriState::kUnset) {
+ break;
+ }
+ }
+
+ *options = local_options;
+}
+
+pid_t ProcessSnapshotWin::ProcessID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return process_reader_.ProcessID();
+}
+
+pid_t ProcessSnapshotWin::ParentProcessID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return process_reader_.ParentProcessID();
+}
+
+void ProcessSnapshotWin::SnapshotTime(timeval* snapshot_time) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *snapshot_time = snapshot_time_;
+}
+
+void ProcessSnapshotWin::ProcessStartTime(timeval* start_time) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ process_reader_.StartTime(start_time);
+}
+
+void ProcessSnapshotWin::ProcessCPUTimes(timeval* user_time,
+ timeval* system_time) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ process_reader_.CPUTimes(user_time, system_time);
+}
+
+void ProcessSnapshotWin::ReportID(UUID* report_id) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *report_id = report_id_;
+}
+
+void ProcessSnapshotWin::ClientID(UUID* client_id) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *client_id = client_id_;
+}
+
+const std::map<std::string, std::string>&
+ProcessSnapshotWin::AnnotationsSimpleMap() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return annotations_simple_map_;
+}
+
+const SystemSnapshot* ProcessSnapshotWin::System() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return &system_;
+}
+
+std::vector<const ThreadSnapshot*> ProcessSnapshotWin::Threads() const {
+ CHECK(false) << "TODO(scottmg)";
+ return std::vector<const ThreadSnapshot*>();
+}
+
+std::vector<const ModuleSnapshot*> ProcessSnapshotWin::Modules() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ std::vector<const ModuleSnapshot*> modules;
+ for (internal::ModuleSnapshotWin* module : modules_) {
+ modules.push_back(module);
+ }
+ return modules;
+}
+
+const ExceptionSnapshot* ProcessSnapshotWin::Exception() const {
+ CHECK(false) << "TODO(scottmg)";
+ return nullptr;
+}
+
+void ProcessSnapshotWin::InitializeModules() {
+ const std::vector<ProcessInfo::Module>& process_reader_modules =
+ process_reader_.Modules();
+ for (const ProcessInfo::Module& process_reader_module :
+ process_reader_modules) {
+ auto module = make_scoped_ptr(new internal::ModuleSnapshotWin());
+ if (module->Initialize(&process_reader_, process_reader_module)) {
+ modules_.push_back(module.release());
+ }
+ }
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.h b/chromium/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.h
new file mode 100644
index 00000000000..8bb0bf48197
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.h
@@ -0,0 +1,128 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_WIN_PROCESS_SNAPSHOT_WIN_H_
+#define CRASHPAD_SNAPSHOT_WIN_PROCESS_SNAPSHOT_WIN_H_
+
+#include <windows.h>
+#include <sys/time.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "client/crashpad_info.h"
+#include "snapshot/crashpad_info_client_options.h"
+#include "snapshot/exception_snapshot.h"
+#include "snapshot/module_snapshot.h"
+#include "snapshot/process_snapshot.h"
+#include "snapshot/system_snapshot.h"
+#include "snapshot/thread_snapshot.h"
+#include "snapshot/win/module_snapshot_win.h"
+#include "snapshot/win/system_snapshot_win.h"
+#include "util/misc/initialization_state_dcheck.h"
+#include "util/misc/uuid.h"
+#include "util/stdlib/pointer_container.h"
+
+namespace crashpad {
+
+//! \brief A ProcessSnapshot of a running (or crashed) process running on a
+//! Windows system.
+class ProcessSnapshotWin final : public ProcessSnapshot {
+ public:
+ ProcessSnapshotWin();
+ ~ProcessSnapshotWin() override;
+
+ //! \brief Initializes the object.
+ //!
+ //! \param[in] process The handle to create a snapshot from.
+ //!
+ //! \return `true` if the snapshot could be created, `false` otherwise with
+ //! an appropriate message logged.
+ bool Initialize(HANDLE process);
+
+ //! \brief Sets the value to be returned by ReportID().
+ //!
+ //! The crash report ID is under the control of the snapshot producer, which
+ //! may call this method to set the report ID. If this is not done, ReportID()
+ //! will return an identifier consisting entirely of zeroes.
+ void SetReportID(const UUID& report_id) { report_id_ = report_id; }
+
+ //! \brief Sets the value to be returned by ClientID().
+ //!
+ //! The client ID is under the control of the snapshot producer, which may
+ //! call this method to set the client ID. If this is not done, ClientID()
+ //! will return an identifier consisting entirely of zeroes.
+ void SetClientID(const UUID& client_id) { client_id_ = client_id; }
+
+ //! \brief Sets the value to be returned by AnnotationsSimpleMap().
+ //!
+ //! All process annotations are under the control of the snapshot producer,
+ //! which may call this method to establish these annotations. Contrast this
+ //! with module annotations, which are under the control of the process being
+ //! snapshotted.
+ void SetAnnotationsSimpleMap(
+ const std::map<std::string, std::string>& annotations_simple_map) {
+ annotations_simple_map_ = annotations_simple_map;
+ }
+
+ //! \brief Returns options from CrashpadInfo structures found in modules in
+ //! the process.
+ //!
+ //! \param[out] options Options set in CrashpadInfo structures in modules in
+ //! the process.
+ void GetCrashpadOptions(CrashpadInfoClientOptions* options);
+
+ // ProcessSnapshot:
+
+ pid_t ProcessID() const override;
+ pid_t ParentProcessID() const override;
+ void SnapshotTime(timeval* snapshot_time) const override;
+ void ProcessStartTime(timeval* start_time) const override;
+ void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override;
+ void ReportID(UUID* report_id) const override;
+ void ClientID(UUID* client_id) const override;
+ const std::map<std::string, std::string>& AnnotationsSimpleMap()
+ const override;
+ const SystemSnapshot* System() const override;
+ std::vector<const ThreadSnapshot*> Threads() const override;
+ std::vector<const ModuleSnapshot*> Modules() const override;
+ const ExceptionSnapshot* Exception() const override;
+
+ private:
+ // Initializes threads_ on behalf of Initialize().
+ // TODO(scottmg): void InitializeThreads();
+
+ // Initializes modules_ on behalf of Initialize().
+ void InitializeModules();
+
+ internal::SystemSnapshotWin system_;
+ // TODO(scottmg): PointerVector<internal::ThreadSnapshotWin> threads_;
+ PointerVector<internal::ModuleSnapshotWin> modules_;
+ // TODO(scottmg): scoped_ptr<internal::ExceptionSnapshotWin> exception_;
+ ProcessReaderWin process_reader_;
+ UUID report_id_;
+ UUID client_id_;
+ std::map<std::string, std::string> annotations_simple_map_;
+ timeval snapshot_time_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotWin);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_WIN_PROCESS_SNAPSHOT_WIN_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/win/system_snapshot_win.cc b/chromium/third_party/crashpad/crashpad/snapshot/win/system_snapshot_win.cc
new file mode 100644
index 00000000000..5e24211a2dd
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/win/system_snapshot_win.cc
@@ -0,0 +1,356 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/win/system_snapshot_win.h"
+
+#include <intrin.h>
+#include <powrprof.h>
+#include <windows.h>
+#include <winnt.h>
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+
+namespace crashpad {
+
+namespace {
+
+//! \brief Gets a string representation for a VS_FIXEDFILEINFO.dwFileFlags
+//! value.
+std::string GetStringForFileFlags(uint32_t file_flags) {
+ std::string result;
+ DCHECK_EQ(file_flags & VS_FF_INFOINFERRED, 0u);
+ if (file_flags & VS_FF_DEBUG)
+ result += "Debug,";
+ if (file_flags & VS_FF_PATCHED)
+ result += "Patched,";
+ if (file_flags & VS_FF_PRERELEASE)
+ result += "Prerelease,";
+ if (file_flags & VS_FF_PRIVATEBUILD)
+ result += "Private,";
+ if (file_flags & VS_FF_SPECIALBUILD)
+ result += "Special,";
+ if (!result.empty())
+ return result.substr(0, result.size() - 1); // Remove trailing comma.
+ return result;
+}
+
+//! \brief Gets a string representation for a VS_FIXEDFILEINFO.dwFileOS value.
+std::string GetStringForFileOS(uint32_t file_os) {
+ // There are a variety of ancient things this could theoretically be. In
+ // practice, we're always going to get VOS_NT_WINDOWS32 here.
+ if ((file_os & VOS_NT_WINDOWS32) == VOS_NT_WINDOWS32)
+ return "Windows NT";
+ else
+ return "Unknown";
+}
+
+} // namespace
+
+namespace internal {
+
+SystemSnapshotWin::SystemSnapshotWin()
+ : SystemSnapshot(),
+ os_version_full_(),
+ os_version_build_(),
+ process_reader_(nullptr),
+ os_version_major_(0),
+ os_version_minor_(0),
+ os_version_bugfix_(0),
+ os_server_(false),
+ initialized_() {
+}
+
+SystemSnapshotWin::~SystemSnapshotWin() {
+}
+
+void SystemSnapshotWin::Initialize(ProcessReaderWin* process_reader) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ process_reader_ = process_reader;
+
+ // We use both GetVersionEx and VerQueryValue. GetVersionEx is not trustworthy
+ // after Windows 8 (depending on the application manifest) so its data is used
+ // only to fill the os_server_ field, and the rest comes from the version
+ // information stamped on kernel32.dll.
+ OSVERSIONINFOEX version_info = {sizeof(version_info)};
+ if (!GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&version_info))) {
+ PLOG(WARNING) << "GetVersionEx";
+ } else {
+ const wchar_t kSystemDll[] = L"kernel32.dll";
+ DWORD size = GetFileVersionInfoSize(kSystemDll, nullptr);
+ if (!size) {
+ PLOG(WARNING) << "GetFileVersionInfoSize";
+ } else {
+ scoped_ptr<uint8_t[]> data(new uint8_t[size]);
+ if (!GetFileVersionInfo(kSystemDll, 0, size, data.get())) {
+ PLOG(WARNING) << "GetFileVersionInfo";
+ } else {
+ VS_FIXEDFILEINFO* fixed_file_info;
+ UINT size;
+ if (!VerQueryValue(data.get(),
+ L"\\",
+ reinterpret_cast<void**>(&fixed_file_info),
+ &size)) {
+ PLOG(WARNING) << "VerQueryValue";
+ } else {
+ uint32_t valid_flags =
+ fixed_file_info->dwFileFlags & fixed_file_info->dwFileFlagsMask;
+ std::string flags_string = GetStringForFileFlags(valid_flags);
+ os_version_major_ =
+ (fixed_file_info->dwFileVersionMS & 0xffff0000) >> 16;
+ os_version_minor_ = fixed_file_info->dwFileVersionMS & 0xffff;
+ os_version_bugfix_ =
+ (fixed_file_info->dwFileVersionLS & 0xffff0000) >> 16;
+ os_version_build_ = base::StringPrintf(
+ "%d", fixed_file_info->dwFileVersionLS & 0xffff);
+ os_server_ = version_info.wProductType != VER_NT_WORKSTATION;
+ std::string os_name = GetStringForFileOS(fixed_file_info->dwFileOS);
+ os_version_full_ = base::StringPrintf(
+ "%s %d.%d.%d.%s%s",
+ os_name.c_str(),
+ os_version_major_,
+ os_version_minor_,
+ os_version_bugfix_,
+ os_version_build_.c_str(),
+ flags_string.empty() ? "" : (std::string(" (") + flags_string +
+ ")").c_str());
+ }
+ }
+ }
+ }
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+}
+
+CPUArchitecture SystemSnapshotWin::GetCPUArchitecture() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ return process_reader_->Is64Bit() ? kCPUArchitectureX86_64
+ : kCPUArchitectureX86;
+}
+
+uint32_t SystemSnapshotWin::CPURevision() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ uint32_t raw = CPUX86Signature();
+ uint8_t stepping = raw & 0xf;
+ uint8_t model = (raw & 0xf0) >> 4;
+ uint8_t family = (raw & 0xf00) >> 8;
+ uint8_t extended_model = static_cast<uint8_t>((raw & 0xf0000) >> 16);
+ uint16_t extended_family = (raw & 0xff00000) >> 20;
+
+ // For families before 15, extended_family are simply reserved bits.
+ if (family < 15)
+ extended_family = 0;
+ // extended_model is only used for families 6 and 15.
+ if (family != 6 && family != 15)
+ extended_model = 0;
+
+ uint16_t adjusted_family = family + extended_family;
+ uint8_t adjusted_model = model + (extended_model << 4);
+ return (adjusted_family << 16) | (adjusted_model << 8) | stepping;
+}
+
+uint8_t SystemSnapshotWin::CPUCount() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ SYSTEM_INFO system_info;
+ GetSystemInfo(&system_info);
+ if (!base::IsValueInRangeForNumericType<uint8_t>(
+ system_info.dwNumberOfProcessors)) {
+ LOG(WARNING) << "dwNumberOfProcessors exceeds uint8_t storage";
+ }
+ return base::saturated_cast<uint8_t>(system_info.dwNumberOfProcessors);
+}
+
+std::string SystemSnapshotWin::CPUVendor() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ int cpu_info[4];
+ __cpuid(cpu_info, 0);
+ char vendor[12];
+ *reinterpret_cast<int*>(vendor) = cpu_info[1];
+ *reinterpret_cast<int*>(vendor + 4) = cpu_info[3];
+ *reinterpret_cast<int*>(vendor + 8) = cpu_info[2];
+ return std::string(vendor, sizeof(vendor));
+}
+
+void SystemSnapshotWin::CPUFrequency(uint64_t* current_hz,
+ uint64_t* max_hz) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ int num_cpus = CPUCount();
+ DCHECK_GT(num_cpus, 0);
+ std::vector<PROCESSOR_POWER_INFORMATION> info(num_cpus);
+ if (CallNtPowerInformation(ProcessorInformation,
+ nullptr,
+ 0,
+ &info[0],
+ sizeof(PROCESSOR_POWER_INFORMATION) * num_cpus) !=
+ 0) {
+ *current_hz = 0;
+ *max_hz = 0;
+ return;
+ }
+ const uint64_t kMhzToHz = static_cast<uint64_t>(1E6);
+ *current_hz = std::max_element(info.begin(),
+ info.end(),
+ [](const PROCESSOR_POWER_INFORMATION& a,
+ const PROCESSOR_POWER_INFORMATION& b) {
+ return a.CurrentMhz < b.CurrentMhz;
+ })->CurrentMhz *
+ kMhzToHz;
+ *max_hz = std::max_element(info.begin(),
+ info.end(),
+ [](const PROCESSOR_POWER_INFORMATION& a,
+ const PROCESSOR_POWER_INFORMATION& b) {
+ return a.MaxMhz < b.MaxMhz;
+ })->MaxMhz *
+ kMhzToHz;
+}
+
+uint32_t SystemSnapshotWin::CPUX86Signature() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ int cpu_info[4];
+ // We will never run on any processors that don't support at least function 1.
+ __cpuid(cpu_info, 1);
+ return cpu_info[0];
+}
+
+uint64_t SystemSnapshotWin::CPUX86Features() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ int cpu_info[4];
+ // We will never run on any processors that don't support at least function 1.
+ __cpuid(cpu_info, 1);
+ return (static_cast<uint64_t>(cpu_info[2]) << 32) |
+ static_cast<uint64_t>(cpu_info[3]);
+}
+
+uint64_t SystemSnapshotWin::CPUX86ExtendedFeatures() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ int cpu_info[4];
+ // We will never run on any processors that don't support at least extended
+ // function 1.
+ __cpuid(cpu_info, 0x80000001);
+ return (static_cast<uint64_t>(cpu_info[2]) << 32) |
+ static_cast<uint64_t>(cpu_info[3]);
+}
+
+uint32_t SystemSnapshotWin::CPUX86Leaf7Features() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ int cpu_info[4];
+
+ // Make sure leaf 7 can be called.
+ __cpuid(cpu_info, 0);
+ if (cpu_info[0] < 7)
+ return 0;
+
+ __cpuidex(cpu_info, 7, 0);
+ return cpu_info[1];
+}
+
+bool SystemSnapshotWin::CPUX86SupportsDAZ() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ // The correct way to check for denormals-as-zeros (DAZ) support is to examine
+ // mxcsr mask, which can be done with fxsave. See Intel Software Developer's
+ // Manual, Volume 1: Basic Architecture (253665-051), 11.6.3 "Checking for the
+ // DAZ Flag in the MXCSR Register". Note that since this function tests for
+ // DAZ support in the CPU, it checks the mxcsr mask. Testing mxcsr would
+ // indicate whether DAZ is actually enabled, which is a per-thread context
+ // concern.
+
+ // Test for fxsave support.
+ uint64_t features = CPUX86Features();
+ if (!(features & (UINT64_C(1) << 24))) {
+ return false;
+ }
+
+ // Call fxsave.
+ __declspec(align(16)) uint32_t extended_registers[128];
+ _fxsave(&extended_registers);
+ uint32_t mxcsr_mask = extended_registers[7];
+
+ // Test the DAZ bit.
+ return mxcsr_mask & (1 << 6);
+}
+
+SystemSnapshot::OperatingSystem SystemSnapshotWin::GetOperatingSystem() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return kOperatingSystemWindows;
+}
+
+bool SystemSnapshotWin::OSServer() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return os_server_;
+}
+
+void SystemSnapshotWin::OSVersion(int* major,
+ int* minor,
+ int* bugfix,
+ std::string* build) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *major = os_version_major_;
+ *minor = os_version_minor_;
+ *bugfix = os_version_bugfix_;
+ build->assign(os_version_build_);
+}
+
+std::string SystemSnapshotWin::OSVersionFull() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return os_version_full_;
+}
+
+std::string SystemSnapshotWin::MachineDescription() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ // TODO(scottmg): Not sure if there's anything sensible to put here.
+ return std::string();
+}
+
+bool SystemSnapshotWin::NXEnabled() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return IsProcessorFeaturePresent(PF_NX_ENABLED);
+}
+
+void SystemSnapshotWin::TimeZone(DaylightSavingTimeStatus* dst_status,
+ int* standard_offset_seconds,
+ int* daylight_offset_seconds,
+ std::string* standard_name,
+ std::string* daylight_name) const {
+ // This returns the current time zone status rather than the status at the
+ // time of the snapshot. This differs from the Mac implementation.
+ TIME_ZONE_INFORMATION time_zone_information;
+ *dst_status = static_cast<DaylightSavingTimeStatus>(
+ GetTimeZoneInformation(&time_zone_information));
+ *standard_offset_seconds =
+ (time_zone_information.Bias + time_zone_information.StandardBias) * -60;
+ *daylight_offset_seconds =
+ (time_zone_information.Bias + time_zone_information.DaylightBias) * -60;
+ *standard_name = base::UTF16ToUTF8(time_zone_information.StandardName);
+ *daylight_name = base::UTF16ToUTF8(time_zone_information.DaylightName);
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/win/system_snapshot_win.h b/chromium/third_party/crashpad/crashpad/snapshot/win/system_snapshot_win.h
new file mode 100644
index 00000000000..b7a22c6ad0b
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/win/system_snapshot_win.h
@@ -0,0 +1,96 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_WIN_SYSTEM_SNAPSHOT_WIN_H_
+#define CRASHPAD_SNAPSHOT_WIN_SYSTEM_SNAPSHOT_WIN_H_
+
+#include <stdint.h>
+#include <sys/time.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "snapshot/system_snapshot.h"
+#include "snapshot/win/process_reader_win.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+
+class ProcessReaderWin;
+
+namespace internal {
+
+//! \brief A SystemSnapshot of the running system, when the system runs Windows.
+class SystemSnapshotWin final : public SystemSnapshot {
+ public:
+ SystemSnapshotWin();
+ ~SystemSnapshotWin() override;
+
+ //! \brief Initializes the object.
+ //!
+ //! \param[in] process_reader A reader for the process being snapshotted.
+ //!
+ //! It seems odd that a system snapshot implementation would need a
+ //! ProcessReaderWin, but some of the information reported about the
+ //! system depends on the process it's being reported for. For example,
+ //! the architecture returned by GetCPUArchitecture() should be the
+ //! architecture of the process, which may be different than the native
+ //! architecture of the system: an x86_64 system can run both x86_64 and
+ //! 32-bit x86 processes.
+ void Initialize(ProcessReaderWin* process_reader);
+
+ // SystemSnapshot:
+
+ CPUArchitecture GetCPUArchitecture() const override;
+ uint32_t CPURevision() const override;
+ uint8_t CPUCount() const override;
+ std::string CPUVendor() const override;
+ void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override;
+ uint32_t CPUX86Signature() const override;
+ uint64_t CPUX86Features() const override;
+ uint64_t CPUX86ExtendedFeatures() const override;
+ uint32_t CPUX86Leaf7Features() const override;
+ bool CPUX86SupportsDAZ() const override;
+ OperatingSystem GetOperatingSystem() const override;
+ bool OSServer() const override;
+ void OSVersion(int* major,
+ int* minor,
+ int* bugfix,
+ std::string* build) const override;
+ std::string OSVersionFull() const override;
+ bool NXEnabled() const override;
+ std::string MachineDescription() const override;
+ void TimeZone(DaylightSavingTimeStatus* dst_status,
+ int* standard_offset_seconds,
+ int* daylight_offset_seconds,
+ std::string* standard_name,
+ std::string* daylight_name) const override;
+
+ private:
+ std::string os_version_full_;
+ std::string os_version_build_;
+ ProcessReaderWin* process_reader_; // weak
+ int os_version_major_;
+ int os_version_minor_;
+ int os_version_bugfix_;
+ bool os_server_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(SystemSnapshotWin);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_WIN_SYSTEM_SNAPSHOT_WIN_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/win/system_snapshot_win_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/win/system_snapshot_win_test.cc
new file mode 100644
index 00000000000..7552ca5642b
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/win/system_snapshot_win_test.cc
@@ -0,0 +1,156 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/win/system_snapshot_win.h"
+
+#include <sys/time.h>
+#include <time.h>
+
+#include <string>
+
+#include "build/build_config.h"
+#include "gtest/gtest.h"
+#include "snapshot/win/process_reader_win.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+class SystemSnapshotWinTest : public testing::Test {
+ public:
+ SystemSnapshotWinTest()
+ : Test(),
+ process_reader_(),
+ system_snapshot_() {
+ }
+
+ const internal::SystemSnapshotWin& system_snapshot() const {
+ return system_snapshot_;
+ }
+
+ // testing::Test:
+ void SetUp() override {
+ ASSERT_TRUE(process_reader_.Initialize(GetCurrentProcess()));
+ system_snapshot_.Initialize(&process_reader_);
+ }
+
+ private:
+ ProcessReaderWin process_reader_;
+ internal::SystemSnapshotWin system_snapshot_;
+
+ DISALLOW_COPY_AND_ASSIGN(SystemSnapshotWinTest);
+};
+
+TEST_F(SystemSnapshotWinTest, GetCPUArchitecture) {
+ CPUArchitecture cpu_architecture = system_snapshot().GetCPUArchitecture();
+
+#if defined(ARCH_CPU_X86)
+ EXPECT_EQ(kCPUArchitectureX86, cpu_architecture);
+#elif defined(ARCH_CPU_X86_64)
+ EXPECT_EQ(kCPUArchitectureX86_64, cpu_architecture);
+#endif
+}
+
+TEST_F(SystemSnapshotWinTest, CPUCount) {
+ EXPECT_GE(system_snapshot().CPUCount(), 1);
+}
+
+TEST_F(SystemSnapshotWinTest, CPUVendor) {
+ std::string cpu_vendor = system_snapshot().CPUVendor();
+
+ // There are a variety of other values, but we don't expect to run our tests
+ // on them.
+ EXPECT_TRUE(cpu_vendor == "GenuineIntel" || cpu_vendor == "AuthenticAMD");
+}
+
+TEST_F(SystemSnapshotWinTest, CPUX86SupportsDAZ) {
+ // Most SSE2+ machines support Denormals-Are-Zero. This may fail if run on
+ // older machines.
+ EXPECT_TRUE(system_snapshot().CPUX86SupportsDAZ());
+}
+
+TEST_F(SystemSnapshotWinTest, GetOperatingSystem) {
+ EXPECT_EQ(SystemSnapshot::kOperatingSystemWindows,
+ system_snapshot().GetOperatingSystem());
+}
+
+TEST_F(SystemSnapshotWinTest, OSVersion) {
+ int major;
+ int minor;
+ int bugfix;
+ std::string build;
+ system_snapshot().OSVersion(&major, &minor, &bugfix, &build);
+
+ EXPECT_GE(major, 5);
+ if (major == 5)
+ EXPECT_GE(minor, 1);
+ if (major == 6)
+ EXPECT_TRUE(minor >= 0 && minor <= 3);
+}
+
+TEST_F(SystemSnapshotWinTest, OSVersionFull) {
+ EXPECT_FALSE(system_snapshot().OSVersionFull().empty());
+}
+
+TEST_F(SystemSnapshotWinTest, MachineDescription) {
+ EXPECT_TRUE(system_snapshot().MachineDescription().empty());
+}
+
+TEST_F(SystemSnapshotWinTest, TimeZone) {
+ SystemSnapshot::DaylightSavingTimeStatus dst_status;
+ int standard_offset_seconds;
+ int daylight_offset_seconds;
+ std::string standard_name;
+ std::string daylight_name;
+
+ system_snapshot().TimeZone(&dst_status,
+ &standard_offset_seconds,
+ &daylight_offset_seconds,
+ &standard_name,
+ &daylight_name);
+
+ // |standard_offset_seconds| gives seconds east of UTC, and |timezone| gives
+ // seconds west of UTC.
+ EXPECT_EQ(-timezone, standard_offset_seconds);
+
+ // In contemporary usage, most time zones have an integer hour offset from
+ // UTC, although several are at a half-hour offset, and two are at 15-minute
+ // offsets. Throughout history, other variations existed. See
+ // http://www.timeanddate.com/time/time-zones-interesting.html.
+ EXPECT_EQ(0, standard_offset_seconds % (15 * 60))
+ << "standard_offset_seconds " << standard_offset_seconds;
+
+ if (dst_status == SystemSnapshot::kDoesNotObserveDaylightSavingTime) {
+ EXPECT_EQ(standard_offset_seconds, daylight_offset_seconds);
+ EXPECT_EQ(standard_name, daylight_name);
+ } else {
+ EXPECT_EQ(0, daylight_offset_seconds % (15 * 60))
+ << "daylight_offset_seconds " << daylight_offset_seconds;
+
+ // In contemporary usage, dst_delta_seconds will almost always be one hour,
+ // except for Lord Howe Island, Australia, which uses a 30-minute
+ // delta. Throughout history, other variations existed. See
+ // http://www.timeanddate.com/time/dst/#brief.
+ int dst_delta_seconds = daylight_offset_seconds - standard_offset_seconds;
+ if (dst_delta_seconds != 60 * 60 && dst_delta_seconds != 30 * 60) {
+ FAIL() << "dst_delta_seconds " << dst_delta_seconds;
+ }
+
+ EXPECT_NE(standard_name, daylight_name);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/test/test.gyp b/chromium/third_party/crashpad/crashpad/test/test.gyp
new file mode 100644
index 00000000000..7c0175ca711
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/test/test.gyp
@@ -0,0 +1,70 @@
+# Copyright 2015 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'includes': [
+ '../build/crashpad.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'crashpad_test',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../compat/compat.gyp:crashpad_compat',
+ '../third_party/gtest/gtest.gyp:gtest',
+ '../third_party/mini_chromium/mini_chromium.gyp:base',
+ '../util/util.gyp:crashpad_util',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'errors.cc',
+ 'errors.h',
+ 'gtest_death_check.h',
+ 'mac/dyld.h',
+ 'mac/mach_errors.cc',
+ 'mac/mach_errors.h',
+ 'mac/mach_multiprocess.cc',
+ 'mac/mach_multiprocess.h',
+ 'multiprocess.h',
+ 'multiprocess_exec.h',
+ 'multiprocess_exec_posix.cc',
+ 'multiprocess_exec_win.cc',
+ 'multiprocess_posix.cc',
+ 'paths.cc',
+ 'paths.h',
+ 'paths_mac.cc',
+ 'paths_win.cc',
+ 'scoped_temp_dir.cc',
+ 'scoped_temp_dir.h',
+ 'scoped_temp_dir_posix.cc',
+ 'scoped_temp_dir_win.cc',
+ 'thread.cc',
+ 'thread.h',
+ 'thread_posix.cc',
+ 'thread_win.cc',
+ ],
+ 'conditions': [
+ ['OS=="mac"', {
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/usr/lib/libbsm.dylib',
+ ],
+ },
+ }],
+ ],
+ },
+ ],
+}
diff --git a/chromium/third_party/crashpad/crashpad/test/test_test.gyp b/chromium/third_party/crashpad/crashpad/test/test_test.gyp
new file mode 100644
index 00000000000..2ed3bfaaf0a
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/test/test_test.gyp
@@ -0,0 +1,52 @@
+# Copyright 2015 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'includes': [
+ '../build/crashpad.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'crashpad_test_test',
+ 'type': 'executable',
+ 'dependencies': [
+ 'crashpad_test_test_multiprocess_exec_test_child',
+ 'test.gyp:crashpad_test',
+ '../compat/compat.gyp:crashpad_compat',
+ '../third_party/gmock/gmock.gyp:gmock',
+ '../third_party/gmock/gmock.gyp:gmock_main',
+ '../third_party/gtest/gtest.gyp:gtest',
+ '../third_party/mini_chromium/mini_chromium.gyp:base',
+ '../util/util.gyp:crashpad_util',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'mac/mach_multiprocess_test.cc',
+ 'multiprocess_exec_test.cc',
+ 'multiprocess_posix_test.cc',
+ 'paths_test.cc',
+ 'scoped_temp_dir_test.cc',
+ ],
+ },
+ {
+ 'target_name': 'crashpad_test_test_multiprocess_exec_test_child',
+ 'type': 'executable',
+ 'sources': [
+ 'multiprocess_exec_test_child.cc',
+ ],
+ },
+ ],
+}
diff --git a/chromium/third_party/crashpad/crashpad/third_party/apple_cctools/README.crashpad b/chromium/third_party/crashpad/crashpad/third_party/apple_cctools/README.crashpad
new file mode 100644
index 00000000000..a2aadf72951
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/third_party/apple_cctools/README.crashpad
@@ -0,0 +1,44 @@
+Name: Apple cctools
+Short Name: cctools
+URL: https://opensource.apple.com/source/cctools/
+URL: https://opensource.apple.com/tarballs/cctools/
+Version: 855 (from Xcode 5.1)
+License: APSL 2.0
+License File: cctools/APPLE_LICENSE
+Security Critical: no
+
+Description:
+cctools contains portions of Apple’s compiler toolchain, including common tools
+like ar, as, nm, strings, and strip, and platform-specific tools like lipo and
+otool. It also contains support libraries such as libmacho, which contains
+interfaces for dealing with Mach-O images.
+
+libmacho is available on Mac OS X as a runtime library that is part of
+libSystem, but versions of libmacho included in operating system versions prior
+to Mac OS X 10.7 did not include the getsectiondata() and getsegmentdata()
+functions. This library is present here to provide implementations of these
+functions for systems that do not have them.
+
+Crashpad code is not expected to use this library directly. It should use the
+getsectiondata() and getsegmentdata() wrappers in compat, which will use
+system-provided implementations if present at runtime, and will otherwise fall
+back to the implementations in this library.
+
+Local Modifications:
+ - Only cctools/APPLE_LICENSE, cctools/libmacho/getsecbyname.c, and
+ cctools/include/mach-o/getsect.h are included.
+ - getsecbyname.c and getsect.h have been trimmed to remove everything other
+ than the getsectiondata() and getsegmentdata() functions. The #include guards
+ in getsect.h have been made unique.
+ - getsectiondata() is renamed to crashpad_getsectiondata(), and
+ getsegmentdata() is renamed to crashpad_getsegmentdata().
+ - These functions are only declared and defined if the deployment target is
+ older than 10.7. This library is not needed otherwise, because in that case,
+ the system always provides implementations in runtime libraries.
+ - Originally, each of these two functions were implemented twice: once for
+ 32-bit code and once for 64-bit code. Aside from the types and constants
+ used, the two implementations were completely identical. This has been
+ simplified to have a shared implementation that relies on local typedefs and
+ constants being defined properly. This change was only made in
+ getsecbyname.c. getsect.h was not changed to avoid leaking new definitions
+ beyond this header.
diff --git a/chromium/third_party/crashpad/crashpad/third_party/apple_cctools/apple_cctools.gyp b/chromium/third_party/crashpad/crashpad/third_party/apple_cctools/apple_cctools.gyp
new file mode 100644
index 00000000000..7e0dfd4327e
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/third_party/apple_cctools/apple_cctools.gyp
@@ -0,0 +1,34 @@
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'apple_cctools',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '../..',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '../..',
+ ],
+ },
+ 'sources': [
+ 'cctools/include/mach-o/getsect.h',
+ 'cctools/libmacho/getsecbyname.c',
+ ],
+ },
+ ],
+}
diff --git a/chromium/third_party/crashpad/crashpad/third_party/apple_cctools/cctools/APPLE_LICENSE b/chromium/third_party/crashpad/crashpad/third_party/apple_cctools/cctools/APPLE_LICENSE
new file mode 100644
index 00000000000..fe81a60cae9
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/third_party/apple_cctools/cctools/APPLE_LICENSE
@@ -0,0 +1,367 @@
+APPLE PUBLIC SOURCE LICENSE
+Version 2.0 - August 6, 2003
+
+Please read this License carefully before downloading this software.
+By downloading or using this software, you are agreeing to be bound by
+the terms of this License. If you do not or cannot agree to the terms
+of this License, please do not download or use the software.
+
+1. General; Definitions. This License applies to any program or other
+work which Apple Computer, Inc. ("Apple") makes publicly available and
+which contains a notice placed by Apple identifying such program or
+work as "Original Code" and stating that it is subject to the terms of
+this Apple Public Source License version 2.0 ("License"). As used in
+this License:
+
+1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is
+the grantor of rights, (i) claims of patents that are now or hereafter
+acquired, owned by or assigned to Apple and (ii) that cover subject
+matter contained in the Original Code, but only to the extent
+necessary to use, reproduce and/or distribute the Original Code
+without infringement; and (b) in the case where You are the grantor of
+rights, (i) claims of patents that are now or hereafter acquired,
+owned by or assigned to You and (ii) that cover subject matter in Your
+Modifications, taken alone or in combination with Original Code.
+
+1.2 "Contributor" means any person or entity that creates or
+contributes to the creation of Modifications.
+
+1.3 "Covered Code" means the Original Code, Modifications, the
+combination of Original Code and any Modifications, and/or any
+respective portions thereof.
+
+1.4 "Externally Deploy" means: (a) to sublicense, distribute or
+otherwise make Covered Code available, directly or indirectly, to
+anyone other than You; and/or (b) to use Covered Code, alone or as
+part of a Larger Work, in any way to provide a service, including but
+not limited to delivery of content, through electronic communication
+with a client other than You.
+
+1.5 "Larger Work" means a work which combines Covered Code or portions
+thereof with code not governed by the terms of this License.
+
+1.6 "Modifications" mean any addition to, deletion from, and/or change
+to, the substance and/or structure of the Original Code, any previous
+Modifications, the combination of Original Code and any previous
+Modifications, and/or any respective portions thereof. When code is
+released as a series of files, a Modification is: (a) any addition to
+or deletion from the contents of a file containing Covered Code;
+and/or (b) any new file or other representation of computer program
+statements that contains any part of Covered Code.
+
+1.7 "Original Code" means (a) the Source Code of a program or other
+work as originally made available by Apple under this License,
+including the Source Code of any updates or upgrades to such programs
+or works made available by Apple under this License, and that has been
+expressly identified by Apple as such in the header file(s) of such
+work; and (b) the object code compiled from such Source Code and
+originally made available by Apple under this License.
+
+1.8 "Source Code" means the human readable form of a program or other
+work that is suitable for making modifications to it, including all
+modules it contains, plus any associated interface definition files,
+scripts used to control compilation and installation of an executable
+(object code).
+
+1.9 "You" or "Your" means an individual or a legal entity exercising
+rights under this License. For legal entities, "You" or "Your"
+includes any entity which controls, is controlled by, or is under
+common control with, You, where "control" means (a) the power, direct
+or indirect, to cause the direction or management of such entity,
+whether by contract or otherwise, or (b) ownership of fifty percent
+(50%) or more of the outstanding shares or beneficial ownership of
+such entity.
+
+2. Permitted Uses; Conditions & Restrictions. Subject to the terms
+and conditions of this License, Apple hereby grants You, effective on
+the date You accept this License and download the Original Code, a
+world-wide, royalty-free, non-exclusive license, to the extent of
+Apple's Applicable Patent Rights and copyrights covering the Original
+Code, to do the following:
+
+2.1 Unmodified Code. You may use, reproduce, display, perform,
+internally distribute within Your organization, and Externally Deploy
+verbatim, unmodified copies of the Original Code, for commercial or
+non-commercial purposes, provided that in each instance:
+
+(a) You must retain and reproduce in all copies of Original Code the
+copyright and other proprietary notices and disclaimers of Apple as
+they appear in the Original Code, and keep intact all notices in the
+Original Code that refer to this License; and
+
+(b) You must include a copy of this License with every copy of Source
+Code of Covered Code and documentation You distribute or Externally
+Deploy, and You may not offer or impose any terms on such Source Code
+that alter or restrict this License or the recipients' rights
+hereunder, except as permitted under Section 6.
+
+2.2 Modified Code. You may modify Covered Code and use, reproduce,
+display, perform, internally distribute within Your organization, and
+Externally Deploy Your Modifications and Covered Code, for commercial
+or non-commercial purposes, provided that in each instance You also
+meet all of these conditions:
+
+(a) You must satisfy all the conditions of Section 2.1 with respect to
+the Source Code of the Covered Code;
+
+(b) You must duplicate, to the extent it does not already exist, the
+notice in Exhibit A in each file of the Source Code of all Your
+Modifications, and cause the modified files to carry prominent notices
+stating that You changed the files and the date of any change; and
+
+(c) If You Externally Deploy Your Modifications, You must make
+Source Code of all Your Externally Deployed Modifications either
+available to those to whom You have Externally Deployed Your
+Modifications, or publicly available. Source Code of Your Externally
+Deployed Modifications must be released under the terms set forth in
+this License, including the license grants set forth in Section 3
+below, for as long as you Externally Deploy the Covered Code or twelve
+(12) months from the date of initial External Deployment, whichever is
+longer. You should preferably distribute the Source Code of Your
+Externally Deployed Modifications electronically (e.g. download from a
+web site).
+
+2.3 Distribution of Executable Versions. In addition, if You
+Externally Deploy Covered Code (Original Code and/or Modifications) in
+object code, executable form only, You must include a prominent
+notice, in the code itself as well as in related documentation,
+stating that Source Code of the Covered Code is available under the
+terms of this License with information on how and where to obtain such
+Source Code.
+
+2.4 Third Party Rights. You expressly acknowledge and agree that
+although Apple and each Contributor grants the licenses to their
+respective portions of the Covered Code set forth herein, no
+assurances are provided by Apple or any Contributor that the Covered
+Code does not infringe the patent or other intellectual property
+rights of any other entity. Apple and each Contributor disclaim any
+liability to You for claims brought by any other entity based on
+infringement of intellectual property rights or otherwise. As a
+condition to exercising the rights and licenses granted hereunder, You
+hereby assume sole responsibility to secure any other intellectual
+property rights needed, if any. For example, if a third party patent
+license is required to allow You to distribute the Covered Code, it is
+Your responsibility to acquire that license before distributing the
+Covered Code.
+
+3. Your Grants. In consideration of, and as a condition to, the
+licenses granted to You under this License, You hereby grant to any
+person or entity receiving or distributing Covered Code under this
+License a non-exclusive, royalty-free, perpetual, irrevocable license,
+under Your Applicable Patent Rights and other intellectual property
+rights (other than patent) owned or controlled by You, to use,
+reproduce, display, perform, modify, sublicense, distribute and
+Externally Deploy Your Modifications of the same scope and extent as
+Apple's licenses under Sections 2.1 and 2.2 above.
+
+4. Larger Works. You may create a Larger Work by combining Covered
+Code with other code not governed by the terms of this License and
+distribute the Larger Work as a single product. In each such instance,
+You must make sure the requirements of this License are fulfilled for
+the Covered Code or any portion thereof.
+
+5. Limitations on Patent License. Except as expressly stated in
+Section 2, no other patent rights, express or implied, are granted by
+Apple herein. Modifications and/or Larger Works may require additional
+patent licenses from Apple which Apple may grant in its sole
+discretion.
+
+6. Additional Terms. You may choose to offer, and to charge a fee for,
+warranty, support, indemnity or liability obligations and/or other
+rights consistent with the scope of the license granted herein
+("Additional Terms") to one or more recipients of Covered Code.
+However, You may do so only on Your own behalf and as Your sole
+responsibility, and not on behalf of Apple or any Contributor. You
+must obtain the recipient's agreement that any such Additional Terms
+are offered by You alone, and You hereby agree to indemnify, defend
+and hold Apple and every Contributor harmless for any liability
+incurred by or claims asserted against Apple or such Contributor by
+reason of any such Additional Terms.
+
+7. Versions of the License. Apple may publish revised and/or new
+versions of this License from time to time. Each version will be given
+a distinguishing version number. Once Original Code has been published
+under a particular version of this License, You may continue to use it
+under the terms of that version. You may also choose to use such
+Original Code under the terms of any subsequent version of this
+License published by Apple. No one other than Apple has the right to
+modify the terms applicable to Covered Code created under this
+License.
+
+8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in
+part pre-release, untested, or not fully tested works. The Covered
+Code may contain errors that could cause failures or loss of data, and
+may be incomplete or contain inaccuracies. You expressly acknowledge
+and agree that use of the Covered Code, or any portion thereof, is at
+Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND
+WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND
+APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE
+PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM
+ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT
+NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF
+MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR
+PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD
+PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST
+INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE
+FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS,
+THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR
+ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO
+ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE
+AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY.
+You acknowledge that the Covered Code is not intended for use in the
+operation of nuclear facilities, aircraft navigation, communication
+systems, or air traffic control machines in which case the failure of
+the Covered Code could lead to death, personal injury, or severe
+physical or environmental damage.
+
+9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO
+EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL,
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING
+TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR
+ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY,
+TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF
+APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY
+REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF
+INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY
+TO YOU. In no event shall Apple's total liability to You for all
+damages (other than as may be required by applicable law) under this
+License exceed the amount of fifty dollars ($50.00).
+
+10. Trademarks. This License does not grant any rights to use the
+trademarks or trade names "Apple", "Apple Computer", "Mac", "Mac OS",
+"QuickTime", "QuickTime Streaming Server" or any other trademarks,
+service marks, logos or trade names belonging to Apple (collectively
+"Apple Marks") or to any trademark, service mark, logo or trade name
+belonging to any Contributor. You agree not to use any Apple Marks in
+or as part of the name of products derived from the Original Code or
+to endorse or promote products derived from the Original Code other
+than as expressly permitted by and in strict compliance at all times
+with Apple's third party trademark usage guidelines which are posted
+at http://www.apple.com/legal/guidelinesfor3rdparties.html.
+
+11. Ownership. Subject to the licenses granted under this License,
+each Contributor retains all rights, title and interest in and to any
+Modifications made by such Contributor. Apple retains all rights,
+title and interest in and to the Original Code and any Modifications
+made by or on behalf of Apple ("Apple Modifications"), and such Apple
+Modifications will not be automatically subject to this License. Apple
+may, at its sole discretion, choose to license such Apple
+Modifications under this License, or on different terms from those
+contained in this License or may choose not to license them at all.
+
+12. Termination.
+
+12.1 Termination. This License and the rights granted hereunder will
+terminate:
+
+(a) automatically without notice from Apple if You fail to comply with
+any term(s) of this License and fail to cure such breach within 30
+days of becoming aware of such breach;
+
+(b) immediately in the event of the circumstances described in Section
+13.5(b); or
+
+(c) automatically without notice from Apple if You, at any time during
+the term of this License, commence an action for patent infringement
+against Apple; provided that Apple did not first commence
+an action for patent infringement against You in that instance.
+
+12.2 Effect of Termination. Upon termination, You agree to immediately
+stop any further use, reproduction, modification, sublicensing and
+distribution of the Covered Code. All sublicenses to the Covered Code
+which have been properly granted prior to termination shall survive
+any termination of this License. Provisions which, by their nature,
+should remain in effect beyond the termination of this License shall
+survive, including but not limited to Sections 3, 5, 8, 9, 10, 11,
+12.2 and 13. No party will be liable to any other for compensation,
+indemnity or damages of any sort solely as a result of terminating
+this License in accordance with its terms, and termination of this
+License will be without prejudice to any other right or remedy of
+any party.
+
+13. Miscellaneous.
+
+13.1 Government End Users. The Covered Code is a "commercial item" as
+defined in FAR 2.101. Government software and technical data rights in
+the Covered Code include only those rights customarily provided to the
+public as defined in this License. This customary commercial license
+in technical data and software is provided in accordance with FAR
+12.211 (Technical Data) and 12.212 (Computer Software) and, for
+Department of Defense purchases, DFAR 252.227-7015 (Technical Data --
+Commercial Items) and 227.7202-3 (Rights in Commercial Computer
+Software or Computer Software Documentation). Accordingly, all U.S.
+Government End Users acquire Covered Code with only those rights set
+forth herein.
+
+13.2 Relationship of Parties. This License will not be construed as
+creating an agency, partnership, joint venture or any other form of
+legal association between or among You, Apple or any Contributor, and
+You will not represent to the contrary, whether expressly, by
+implication, appearance or otherwise.
+
+13.3 Independent Development. Nothing in this License will impair
+Apple's right to acquire, license, develop, have others develop for
+it, market and/or distribute technology or products that perform the
+same or similar functions as, or otherwise compete with,
+Modifications, Larger Works, technology or products that You may
+develop, produce, market or distribute.
+
+13.4 Waiver; Construction. Failure by Apple or any Contributor to
+enforce any provision of this License will not be deemed a waiver of
+future enforcement of that or any other provision. Any law or
+regulation which provides that the language of a contract shall be
+construed against the drafter will not apply to this License.
+
+13.5 Severability. (a) If for any reason a court of competent
+jurisdiction finds any provision of this License, or portion thereof,
+to be unenforceable, that provision of the License will be enforced to
+the maximum extent permissible so as to effect the economic benefits
+and intent of the parties, and the remainder of this License will
+continue in full force and effect. (b) Notwithstanding the foregoing,
+if applicable law prohibits or restricts You from fully and/or
+specifically complying with Sections 2 and/or 3 or prevents the
+enforceability of either of those Sections, this License will
+immediately terminate and You must immediately discontinue any use of
+the Covered Code and destroy all copies of it that are in your
+possession or control.
+
+13.6 Dispute Resolution. Any litigation or other dispute resolution
+between You and Apple relating to this License shall take place in the
+Northern District of California, and You and Apple hereby consent to
+the personal jurisdiction of, and venue in, the state and federal
+courts within that District with respect to this License. The
+application of the United Nations Convention on Contracts for the
+International Sale of Goods is expressly excluded.
+
+13.7 Entire Agreement; Governing Law. This License constitutes the
+entire agreement between the parties with respect to the subject
+matter hereof. This License shall be governed by the laws of the
+United States and the State of California, except that body of
+California law concerning conflicts of law.
+
+Where You are located in the province of Quebec, Canada, the following
+clause applies: The parties hereby confirm that they have requested
+that this License and all related documents be drafted in English. Les
+parties ont exige que le present contrat et tous les documents
+connexes soient rediges en anglais.
+
+EXHIBIT A.
+
+"Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights
+Reserved.
+
+This file contains Original Code and/or Modifications of Original Code
+as defined in and that are subject to the Apple Public Source License
+Version 2.0 (the 'License'). You may not use this file except in
+compliance with the License. Please obtain a copy of the License at
+http://www.opensource.apple.com/apsl/ and read it before using this
+file.
+
+The Original Code and all software distributed under the License are
+distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+Please see the License for the specific language governing rights and
+limitations under the License."
diff --git a/chromium/third_party/crashpad/crashpad/third_party/apple_cctools/cctools/include/mach-o/getsect.h b/chromium/third_party/crashpad/crashpad/third_party/apple_cctools/cctools/include/mach-o/getsect.h
new file mode 100644
index 00000000000..639b80611d2
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/third_party/apple_cctools/cctools/include/mach-o/getsect.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#ifndef CRASHPAD_THIRD_PARTY_APPLE_CCTOOLS_CCTOOLS_INCLUDE_MACH_O_GETSECT_H_
+#define CRASHPAD_THIRD_PARTY_APPLE_CCTOOLS_CCTOOLS_INCLUDE_MACH_O_GETSECT_H_
+
+#include <AvailabilityMacros.h>
+
+#if !defined(MAC_OS_X_VERSION_10_7) || \
+ MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
+
+#include <stdint.h>
+#include <mach-o/loader.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifndef __LP64__
+/*
+ * Runtime interfaces for 32-bit Mach-O programs.
+ */
+extern uint8_t *crashpad_getsectiondata(
+ const struct mach_header *mhp,
+ const char *segname,
+ const char *sectname,
+ unsigned long *size);
+
+extern uint8_t *crashpad_getsegmentdata(
+ const struct mach_header *mhp,
+ const char *segname,
+ unsigned long *size);
+
+#else /* defined(__LP64__) */
+/*
+ * Runtime interfaces for 64-bit Mach-O programs.
+ */
+extern uint8_t *crashpad_getsectiondata(
+ const struct mach_header_64 *mhp,
+ const char *segname,
+ const char *sectname,
+ unsigned long *size);
+
+extern uint8_t *crashpad_getsegmentdata(
+ const struct mach_header_64 *mhp,
+ const char *segname,
+ unsigned long *size);
+
+#endif /* defined(__LP64__) */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 */
+
+#endif /* CRASHPAD_THIRD_PARTY_APPLE_CCTOOLS_CCTOOLS_INCLUDE_MACH_O_GETSECT_H_ */
diff --git a/chromium/third_party/crashpad/crashpad/third_party/apple_cctools/cctools/libmacho/getsecbyname.c b/chromium/third_party/crashpad/crashpad/third_party/apple_cctools/cctools/libmacho/getsecbyname.c
new file mode 100644
index 00000000000..1db1cbecce3
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/third_party/apple_cctools/cctools/libmacho/getsecbyname.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include "third_party/apple_cctools/cctools/include/mach-o/getsect.h"
+
+#if !defined(MAC_OS_X_VERSION_10_7) || \
+ MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
+
+#include <string.h>
+
+#ifndef __LP64__
+typedef struct mach_header mach_header_32_64;
+typedef struct segment_command segment_command_32_64;
+typedef struct section section_32_64;
+#define LC_SEGMENT_32_64 LC_SEGMENT
+#else /* defined(__LP64__) */
+typedef struct mach_header_64 mach_header_32_64;
+typedef struct segment_command_64 segment_command_32_64;
+typedef struct section_64 section_32_64;
+#define LC_SEGMENT_32_64 LC_SEGMENT_64
+#endif /* defined(__LP64__) */
+
+/*
+ * This routine returns the a pointer to the section contents of the named
+ * section in the named segment if it exists in the image pointed to by the
+ * mach header. Otherwise it returns zero.
+ */
+
+uint8_t *
+crashpad_getsectiondata(
+const mach_header_32_64 *mhp,
+const char *segname,
+const char *sectname,
+unsigned long *size)
+{
+ segment_command_32_64 *sgp, *zero;
+ section_32_64 *sp, *find;
+ uint32_t i, j;
+
+ zero = 0;
+ find = 0;
+ sp = 0;
+ sgp = (segment_command_32_64 *)
+ ((char *)mhp + sizeof(mach_header_32_64));
+ for(i = 0; i < mhp->ncmds; i++){
+ if(sgp->cmd == LC_SEGMENT_32_64){
+ if(zero == 0 && sgp->fileoff == 0 && sgp->nsects != 0){
+ zero = sgp;
+ if(find != 0)
+ goto done;
+ }
+ if(find == 0 &&
+ strncmp(sgp->segname, segname, sizeof(sgp->segname)) == 0){
+ sp = (section_32_64 *)((char *)sgp +
+ sizeof(segment_command_32_64));
+ for(j = 0; j < sgp->nsects; j++){
+ if(strncmp(sp->sectname, sectname,
+ sizeof(sp->sectname)) == 0 &&
+ strncmp(sp->segname, segname,
+ sizeof(sp->segname)) == 0){
+ find = sp;
+ if(zero != 0)
+ goto done;
+ }
+ sp = (section_32_64 *)((char *)sp +
+ sizeof(section_32_64));
+ }
+ }
+ }
+ sgp = (segment_command_32_64 *)((char *)sgp + sgp->cmdsize);
+ }
+ return(0);
+done:
+ *size = sp->size;
+ return((uint8_t *)((uintptr_t)mhp - zero->vmaddr + sp->addr));
+}
+
+uint8_t *
+crashpad_getsegmentdata(
+const mach_header_32_64 *mhp,
+const char *segname,
+unsigned long *size)
+{
+ segment_command_32_64 *sgp, *zero, *find;
+ uint32_t i;
+
+ zero = 0;
+ find = 0;
+ sgp = (segment_command_32_64 *)
+ ((char *)mhp + sizeof(mach_header_32_64));
+ for(i = 0; i < mhp->ncmds; i++){
+ if(sgp->cmd == LC_SEGMENT_32_64){
+ if(zero == 0 && sgp->fileoff == 0 && sgp->nsects != 0){
+ zero = sgp;
+ if(find != 0)
+ goto done;
+ }
+ if(find == 0 &&
+ strncmp(sgp->segname, segname, sizeof(sgp->segname)) == 0){
+ find = sgp;
+ if(zero != 0)
+ goto done;
+ }
+ }
+ sgp = (segment_command_32_64 *)((char *)sgp + sgp->cmdsize);
+ }
+ return(0);
+done:
+ *size = sgp->vmsize;
+ return((uint8_t *)((uintptr_t)mhp - zero->vmaddr + sgp->vmaddr));
+}
+
+#endif /* MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 */
diff --git a/chromium/third_party/crashpad/crashpad/third_party/apple_cf/APPLE_LICENSE b/chromium/third_party/crashpad/crashpad/third_party/apple_cf/APPLE_LICENSE
new file mode 100644
index 00000000000..71fe6fd79ce
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/third_party/apple_cf/APPLE_LICENSE
@@ -0,0 +1,335 @@
+APPLE PUBLIC SOURCE LICENSE
+Version 2.0 - August 6, 2003
+
+Please read this License carefully before downloading this software. By
+downloading or using this software, you are agreeing to be bound by the terms
+of this License. If you do not or cannot agree to the terms of this License,
+please do not download or use the software.
+
+Apple Note: In January 2007, Apple changed its corporate name from "Apple
+Computer, Inc." to "Apple Inc." This change has been reflected below and
+copyright years updated, but no other changes have been made to the APSL 2.0.
+
+1. General; Definitions. This License applies to any program or other
+work which Apple Inc. ("Apple") makes publicly available and which contains a
+notice placed by Apple identifying such program or work as "Original Code" and
+stating that it is subject to the terms of this Apple Public Source License
+version 2.0 ("License"). As used in this License:
+
+1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is the
+grantor of rights, (i) claims of patents that are now or hereafter acquired,
+owned by or assigned to Apple and (ii) that cover subject matter contained in
+the Original Code, but only to the extent necessary to use, reproduce and/or
+distribute the Original Code without infringement; and (b) in the case where
+You are the grantor of rights, (i) claims of patents that are now or hereafter
+acquired, owned by or assigned to You and (ii) that cover subject matter in
+Your Modifications, taken alone or in combination with Original Code.
+
+1.2 "Contributor" means any person or entity that creates or contributes to
+the creation of Modifications.
+
+1.3 "Covered Code" means the Original Code, Modifications, the combination
+of Original Code and any Modifications, and/or any respective portions thereof.
+
+1.4 "Externally Deploy" means: (a) to sublicense, distribute or otherwise
+make Covered Code available, directly or indirectly, to anyone other than You;
+and/or (b) to use Covered Code, alone or as part of a Larger Work, in any way
+to provide a service, including but not limited to delivery of content, through
+electronic communication with a client other than You.
+
+1.5 "Larger Work" means a work which combines Covered Code or portions
+thereof with code not governed by the terms of this License.
+
+1.6 "Modifications" mean any addition to, deletion from, and/or change to,
+the substance and/or structure of the Original Code, any previous
+Modifications, the combination of Original Code and any previous Modifications,
+and/or any respective portions thereof. When code is released as a series of
+files, a Modification is: (a) any addition to or deletion from the contents of
+a file containing Covered Code; and/or (b) any new file or other representation
+of computer program statements that contains any part of Covered Code.
+
+1.7 "Original Code" means (a) the Source Code of a program or other work as
+originally made available by Apple under this License, including the Source
+Code of any updates or upgrades to such programs or works made available by
+Apple under this License, and that has been expressly identified by Apple as
+such in the header file(s) of such work; and (b) the object code compiled from
+such Source Code and originally made available by Apple under this License
+
+1.8 "Source Code" means the human readable form of a program or other work
+that is suitable for making modifications to it, including all modules it
+contains, plus any associated interface definition files, scripts used to
+control compilation and installation of an executable (object code).
+
+1.9 "You" or "Your" means an individual or a legal entity exercising rights
+under this License. For legal entities, "You" or "Your" includes any entity
+which controls, is controlled by, or is under common control with, You, where
+"control" means (a) the power, direct or indirect, to cause the direction or
+management of such entity, whether by contract or otherwise, or (b) ownership
+of fifty percent (50%) or more of the outstanding shares or beneficial
+ownership of such entity.
+
+2. Permitted Uses; Conditions & Restrictions. Subject to the terms and
+conditions of this License, Apple hereby grants You, effective on the date You
+accept this License and download the Original Code, a world-wide, royalty-free,
+non-exclusive license, to the extent of Apple's Applicable Patent Rights and
+copyrights covering the Original Code, to do the following:
+
+2.1 Unmodified Code. You may use, reproduce, display, perform, internally
+distribute within Your organization, and Externally Deploy verbatim, unmodified
+copies of the Original Code, for commercial or non-commercial purposes,
+provided that in each instance:
+
+(a) You must retain and reproduce in all copies of Original Code the
+copyright and other proprietary notices and disclaimers of Apple as they appear
+in the Original Code, and keep intact all notices in the Original Code that
+refer to this License; and
+
+(b) You must include a copy of this License with every copy of Source Code
+of Covered Code and documentation You distribute or Externally Deploy, and You
+may not offer or impose any terms on such Source Code that alter or restrict
+this License or the recipients' rights hereunder, except as permitted under
+Section 6.
+
+2.2 Modified Code. You may modify Covered Code and use, reproduce,
+display, perform, internally distribute within Your organization, and
+Externally Deploy Your Modifications and Covered Code, for commercial or
+non-commercial purposes, provided that in each instance You also meet all of
+these conditions:
+
+(a) You must satisfy all the conditions of Section 2.1 with respect to the
+Source Code of the Covered Code;
+
+(b) You must duplicate, to the extent it does not already exist, the notice
+in Exhibit A in each file of the Source Code of all Your Modifications, and
+cause the modified files to carry prominent notices stating that You changed
+the files and the date of any change; and
+
+(c) If You Externally Deploy Your Modifications, You must make Source Code
+of all Your Externally Deployed Modifications either available to those to whom
+You have Externally Deployed Your Modifications, or publicly available. Source
+Code of Your Externally Deployed Modifications must be released under the terms
+set forth in this License, including the license grants set forth in Section 3
+below, for as long as you Externally Deploy the Covered Code or twelve (12)
+months from the date of initial External Deployment, whichever is longer. You
+should preferably distribute the Source Code of Your Externally Deployed
+Modifications electronically (e.g. download from a web site).
+
+2.3 Distribution of Executable Versions. In addition, if You Externally
+Deploy Covered Code (Original Code and/or Modifications) in object code,
+executable form only, You must include a prominent notice, in the code itself
+as well as in related documentation, stating that Source Code of the Covered
+Code is available under the terms of this License with information on how and
+where to obtain such Source Code.
+
+2.4 Third Party Rights. You expressly acknowledge and agree that although
+Apple and each Contributor grants the licenses to their respective portions of
+the Covered Code set forth herein, no assurances are provided by Apple or any
+Contributor that the Covered Code does not infringe the patent or other
+intellectual property rights of any other entity. Apple and each Contributor
+disclaim any liability to You for claims brought by any other entity based on
+infringement of intellectual property rights or otherwise. As a condition to
+exercising the rights and licenses granted hereunder, You hereby assume sole
+responsibility to secure any other intellectual property rights needed, if any.
+For example, if a third party patent license is required to allow You to
+distribute the Covered Code, it is Your responsibility to acquire that license
+before distributing the Covered Code.
+
+3. Your Grants. In consideration of, and as a condition to, the licenses
+granted to You under this License, You hereby grant to any person or entity
+receiving or distributing Covered Code under this License a non-exclusive,
+royalty-free, perpetual, irrevocable license, under Your Applicable Patent
+Rights and other intellectual property rights (other than patent) owned or
+controlled by You, to use, reproduce, display, perform, modify, sublicense,
+distribute and Externally Deploy Your Modifications of the same scope and
+extent as Apple's licenses under Sections 2.1 and 2.2 above.
+
+4. Larger Works. You may create a Larger Work by combining Covered Code
+with other code not governed by the terms of this License and distribute the
+Larger Work as a single product. In each such instance, You must make sure the
+requirements of this License are fulfilled for the Covered Code or any portion
+thereof.
+
+5. Limitations on Patent License. Except as expressly stated in Section
+2, no other patent rights, express or implied, are granted by Apple herein.
+Modifications and/or Larger Works may require additional patent licenses from
+Apple which Apple may grant in its sole discretion.
+
+6. Additional Terms. You may choose to offer, and to charge a fee for,
+warranty, support, indemnity or liability obligations and/or other rights
+consistent with the scope of the license granted herein ("Additional Terms") to
+one or more recipients of Covered Code. However, You may do so only on Your own
+behalf and as Your sole responsibility, and not on behalf of Apple or any
+Contributor. You must obtain the recipient's agreement that any such Additional
+Terms are offered by You alone, and You hereby agree to indemnify, defend and
+hold Apple and every Contributor harmless for any liability incurred by or
+claims asserted against Apple or such Contributor by reason of any such
+Additional Terms.
+
+7. Versions of the License. Apple may publish revised and/or new versions
+of this License from time to time. Each version will be given a distinguishing
+version number. Once Original Code has been published under a particular
+version of this License, You may continue to use it under the terms of that
+version. You may also choose to use such Original Code under the terms of any
+subsequent version of this License published by Apple. No one other than Apple
+has the right to modify the terms applicable to Covered Code created under this
+License.
+
+8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in
+part pre-release, untested, or not fully tested works. The Covered Code may
+contain errors that could cause failures or loss of data, and may be incomplete
+or contain inaccuracies. You expressly acknowledge and agree that use of the
+Covered Code, or any portion thereof, is at Your sole and entire risk. THE
+COVERED CODE IS PROVIDED "AS IS" AND WITHOUT WARRANTY, UPGRADES OR SUPPORT OF
+ANY KIND AND APPLE AND APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE"
+FOR THE PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM
+ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF MERCHANTABILITY, OF
+SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY, OF
+QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. APPLE AND EACH
+CONTRIBUTOR DOES NOT WARRANT AGAINST INTERFERENCE WITH YOUR ENJOYMENT OF THE
+COVERED CODE, THAT THE FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR
+REQUIREMENTS, THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR
+ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO ORAL OR
+WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE AUTHORIZED
+REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. You acknowledge
+that the Covered Code is not intended for use in the operation of nuclear
+facilities, aircraft navigation, communication systems, or air traffic control
+machines in which case the failure of the Covered Code could lead to death,
+personal injury, or severe physical or environmental damage.
+
+9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO
+EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, SPECIAL,
+INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING TO THIS LICENSE OR
+YOUR USE OR INABILITY TO USE THE COVERED CODE, OR ANY PORTION THEREOF, WHETHER
+UNDER A THEORY OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), PRODUCTS
+LIABILITY OR OTHERWISE, EVEN IF APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL
+PURPOSE OF ANY REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF
+LIABILITY OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT
+APPLY TO YOU. In no event shall Apple's total liability to You for all damages
+(other than as may be required by applicable law) under this License exceed the
+amount of fifty dollars ($50.00).
+
+10. Trademarks. This License does not grant any rights to use the
+trademarks or trade names "Apple", "Mac", "Mac OS", "QuickTime", "QuickTime
+Streaming Server" or any other trademarks, service marks, logos or trade names
+belonging to Apple (collectively "Apple Marks") or to any trademark, service
+mark, logo or trade name belonging to any Contributor. You agree not to use
+any Apple Marks in or as part of the name of products derived from the Original
+Code or to endorse or promote products derived from the Original Code other
+than as expressly permitted by and in strict compliance at all times with
+Apple's third party trademark usage guidelines which are posted at
+http://www.apple.com/legal/guidelinesfor3rdparties.html.
+
+11. Ownership. Subject to the licenses granted under this License, each
+Contributor retains all rights, title and interest in and to any Modifications
+made by such Contributor. Apple retains all rights, title and interest in and
+to the Original Code and any Modifications made by or on behalf of Apple
+("Apple Modifications"), and such Apple Modifications will not be automatically
+subject to this License. Apple may, at its sole discretion, choose to license
+such Apple Modifications under this License, or on different terms from those
+contained in this License or may choose not to license them at all.
+
+12. Termination.
+
+12.1 Termination. This License and the rights granted hereunder will
+terminate:
+
+(a) automatically without notice from Apple if You fail to comply with any
+term(s) of this License and fail to cure such breach within 30 days of becoming
+aware of such breach;
+(b) immediately in the event of the circumstances described in Section
+13.5(b); or
+(c) automatically without notice from Apple if You, at any time during the
+term of this License, commence an action for patent infringement against Apple;
+provided that Apple did not first commence an action for patent infringement
+against You in that instance.
+
+12.2 Effect of Termination. Upon termination, You agree to immediately stop
+any further use, reproduction, modification, sublicensing and distribution of
+the Covered Code. All sublicenses to the Covered Code which have been properly
+granted prior to termination shall survive any termination of this License.
+Provisions which, by their nature, should remain in effect beyond the
+termination of this License shall survive, including but not limited to
+Sections 3, 5, 8, 9, 10, 11, 12.2 and 13. No party will be liable to any other
+for compensation, indemnity or damages of any sort solely as a result of
+terminating this License in accordance with its terms, and termination of this
+License will be without prejudice to any other right or remedy of any party.
+
+13. Miscellaneous.
+
+13.1 Government End Users. The Covered Code is a "commercial item" as
+defined in FAR 2.101. Government software and technical data rights in the
+Covered Code include only those rights customarily provided to the public as
+defined in this License. This customary commercial license in technical data
+and software is provided in accordance with FAR 12.211 (Technical Data) and
+12.212 (Computer Software) and, for Department of Defense purchases, DFAR
+252.227-7015 (Technical Data -- Commercial Items) and 227.7202-3 (Rights in
+Commercial Computer Software or Computer Software Documentation). Accordingly,
+all U.S. Government End Users acquire Covered Code with only those rights set
+forth herein.
+
+13.2 Relationship of Parties. This License will not be construed as
+creating an agency, partnership, joint venture or any other form of legal
+association between or among You, Apple or any Contributor, and You will not
+represent to the contrary, whether expressly, by implication, appearance or
+otherwise.
+
+13.3 Independent Development. Nothing in this License will impair Apple's
+right to acquire, license, develop, have others develop for it, market and/or
+distribute technology or products that perform the same or similar functions
+as, or otherwise compete with, Modifications, Larger Works, technology or
+products that You may develop, produce, market or distribute.
+
+13.4 Waiver; Construction. Failure by Apple or any Contributor to enforce
+any provision of this License will not be deemed a waiver of future enforcement
+of that or any other provision. Any law or regulation which provides that the
+language of a contract shall be construed against the drafter will not apply to
+this License.
+
+13.5 Severability. (a) If for any reason a court of competent jurisdiction
+finds any provision of this License, or portion thereof, to be unenforceable,
+that provision of the License will be enforced to the maximum extent
+permissible so as to effect the economic benefits and intent of the parties,
+and the remainder of this License will continue in full force and effect. (b)
+Notwithstanding the foregoing, if applicable law prohibits or restricts You
+from fully and/or specifically complying with Sections 2 and/or 3 or prevents
+the enforceability of either of those Sections, this License will immediately
+terminate and You must immediately discontinue any use of the Covered Code and
+destroy all copies of it that are in your possession or control.
+
+13.6 Dispute Resolution. Any litigation or other dispute resolution between
+You and Apple relating to this License shall take place in the Northern
+District of California, and You and Apple hereby consent to the personal
+jurisdiction of, and venue in, the state and federal courts within that
+District with respect to this License. The application of the United Nations
+Convention on Contracts for the International Sale of Goods is expressly
+excluded.
+
+13.7 Entire Agreement; Governing Law. This License constitutes the entire
+agreement between the parties with respect to the subject matter hereof. This
+License shall be governed by the laws of the United States and the State of
+California, except that body of California law concerning conflicts of law.
+
+Where You are located in the province of Quebec, Canada, the following clause
+applies: The parties hereby confirm that they have requested that this License
+and all related documents be drafted in English. Les parties ont exigé que le
+présent contrat et tous les documents connexes soient rédigés en anglais.
+
+EXHIBIT A.
+
+"Portions Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
+
+This file contains Original Code and/or Modifications of Original Code as
+defined in and that are subject to the Apple Public Source License Version 2.0
+(the 'License'). You may not use this file except in compliance with the
+License. Please obtain a copy of the License at
+http://www.opensource.apple.com/apsl/ and read it before using this file.
+
+The Original Code and all software distributed under the License are
+distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
+OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
+LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
+specific language governing rights and limitations under the License."
+
diff --git a/chromium/third_party/crashpad/crashpad/third_party/apple_cf/CFStreamAbstract.h b/chromium/third_party/crashpad/crashpad/third_party/apple_cf/CFStreamAbstract.h
new file mode 100644
index 00000000000..bf12c928cf9
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/third_party/apple_cf/CFStreamAbstract.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/* CFStreamAbstract.h
+ Copyright (c) 2000-2009, Apple Inc. All rights reserved.
+*/
+
+#if !defined(__COREFOUNDATION_CFSTREAMABSTRACT__)
+#define __COREFOUNDATION_CFSTREAMABSTRACT__ 1
+
+#include <CoreFoundation/CFStream.h>
+
+CF_EXTERN_C_BEGIN
+
+/* During a stream's lifetime, the open callback will be called once, followed by any number of openCompleted calls (until openCompleted returns TRUE). Then any number of read/canRead or write/canWrite calls, then a single close call. copyProperty can be called at any time. prepareAsynch will be called exactly once when the stream's client is first configured.
+
+ Expected semantics:
+ - open reserves any system resources that are needed. The stream may start the process of opening, returning TRUE immediately and setting openComplete to FALSE. When the open completes, _CFStreamSignalEvent should be called passing kCFStreamOpenCompletedEvent. openComplete should be set to TRUE only if the open operation completed in its entirety.
+ - openCompleted will only be called after open has been called, but before any kCFStreamOpenCompletedEvent has been received. Return TRUE, setting error.code to 0, if the open operation has completed. Return TRUE, setting error to the correct error code and domain if the open operation completed, but failed. Return FALSE if the open operation is still in-progress. If your open ever fails to complete (i.e. sets openComplete to FALSE), you must be implement the openCompleted callback.
+ - read should read into the given buffer, returning the number of bytes successfully read. read must block until at least one byte is available, but should not block until the entire buffer is filled; zero should only be returned if end-of-stream is encountered. atEOF should be set to true if the EOF is encountered, false otherwise. error.code should be set to zero if no error occurs; otherwise, error should be set to the appropriate values.
+ - getBuffer is an optimization to return an internal buffer of bytes read from the stream, and may return NULL. getBuffer itself may be NULL if the concrete implementation does not wish to provide an internal buffer. If implemented, it should set numBytesRead to the number of bytes available in the internal buffer (but should not exceed maxBytesToRead) and return a pointer to the base of the bytes.
+ - canRead will only be called once openCompleted reports that the stream has been successfully opened (or the initial open call succeeded). It should return whether there are bytes that can be read without blocking.
+ - write should write the bytes in the given buffer to the device, returning the number of bytes successfully written. write must block until at least one byte is written. error.code should be set to zero if no error occurs; otherwise, error should be set to the appropriate values.
+ - close should close the device, releasing any reserved system resources. close cannot fail (it may be called to abort the stream), and may be called at any time after open has been called. It will only be called once.
+ - copyProperty should return the value for the given property, or NULL if none exists. Composite streams (streams built on top of other streams) should take care to call CFStreamCopyProperty on the base stream if they do not recognize the property given, to give the underlying stream a chance to respond.
+
+ In all cases, errors returned by reference will be initialized to NULL by the caller, and if they are set to non-NULL, will
+ be released by the caller
+*/
+
+typedef struct {
+ CFIndex version; /* == 2 */
+
+ void *(*create)(CFReadStreamRef stream, void *info);
+ void (*finalize)(CFReadStreamRef stream, void *info);
+ CFStringRef (*copyDescription)(CFReadStreamRef stream, void *info);
+
+ Boolean (*open)(CFReadStreamRef stream, CFErrorRef *error, Boolean *openComplete, void *info);
+ Boolean (*openCompleted)(CFReadStreamRef stream, CFErrorRef *error, void *info);
+ CFIndex (*read)(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFErrorRef *error, Boolean *atEOF, void *info);
+ const UInt8 *(*getBuffer)(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFErrorRef *error, Boolean *atEOF, void *info);
+ Boolean (*canRead)(CFReadStreamRef stream, CFErrorRef *error, void *info);
+ void (*close)(CFReadStreamRef stream, void *info);
+
+ CFTypeRef (*copyProperty)(CFReadStreamRef stream, CFStringRef propertyName, void *info);
+ Boolean (*setProperty)(CFReadStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue, void *info);
+
+ void (*requestEvents)(CFReadStreamRef stream, CFOptionFlags streamEvents, void *info);
+ void (*schedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info);
+ void (*unschedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info);
+} CFReadStreamCallBacks;
+
+typedef struct {
+ CFIndex version; /* == 2 */
+
+ void *(*create)(CFWriteStreamRef stream, void *info);
+ void (*finalize)(CFWriteStreamRef stream, void *info);
+ CFStringRef (*copyDescription)(CFWriteStreamRef stream, void *info);
+
+ Boolean (*open)(CFWriteStreamRef stream, CFErrorRef *error, Boolean *openComplete, void *info);
+ Boolean (*openCompleted)(CFWriteStreamRef stream, CFErrorRef *error, void *info);
+ CFIndex (*write)(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFErrorRef *error, void *info);
+ Boolean (*canWrite)(CFWriteStreamRef stream, CFErrorRef *error, void *info);
+ void (*close)(CFWriteStreamRef stream, void *info);
+
+ CFTypeRef (*copyProperty)(CFWriteStreamRef stream, CFStringRef propertyName, void *info);
+ Boolean (*setProperty)(CFWriteStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue, void *info);
+
+ void (*requestEvents)(CFWriteStreamRef stream, CFOptionFlags streamEvents, void *info);
+ void (*schedule)(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info);
+ void (*unschedule)(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info);
+} CFWriteStreamCallBacks;
+
+// Primitive creation mechanisms.
+CF_EXPORT
+CFReadStreamRef CFReadStreamCreate(CFAllocatorRef alloc, const CFReadStreamCallBacks *callbacks, void *info);
+CF_EXPORT
+CFWriteStreamRef CFWriteStreamCreate(CFAllocatorRef alloc, const CFWriteStreamCallBacks *callbacks, void *info);
+
+/* All the functions below can only be called when you are sure the stream in question was created via
+ CFReadStreamCreate() or CFWriteStreamCreate(), above. They are NOT safe for toll-free bridged objects,
+ so the caller must be sure the argument passed is not such an object. */
+
+// To be called by the concrete stream implementation (the callbacks) when an event occurs. error may be NULL if event != kCFStreamEventErrorOccurred
+// error should be a CFErrorRef if the callbacks are version 2 or later; otherwise it should be a (CFStreamError *).
+CF_EXPORT
+void CFReadStreamSignalEvent(CFReadStreamRef stream, CFStreamEventType event, const void *error);
+CF_EXPORT
+void CFWriteStreamSignalEvent(CFWriteStreamRef stream, CFStreamEventType event, const void *error);
+
+// These require that the stream allow the run loop to run once before delivering the event to its client.
+// See the comment above CFRead/WriteStreamSignalEvent for interpretation of the error argument.
+CF_EXPORT
+void _CFReadStreamSignalEventDelayed(CFReadStreamRef stream, CFStreamEventType event, const void *error);
+CF_EXPORT
+void _CFWriteStreamSignalEventDelayed(CFWriteStreamRef stream, CFStreamEventType event, const void *error);
+
+CF_EXPORT
+void _CFReadStreamClearEvent(CFReadStreamRef stream, CFStreamEventType event);
+// Write variant not currently needed
+//CF_EXPORT
+//void _CFWriteStreamClearEvent(CFWriteStreamRef stream, CFStreamEventType event);
+
+// Convenience for concrete implementations to extract the info pointer given the stream.
+CF_EXPORT
+void *CFReadStreamGetInfoPointer(CFReadStreamRef stream);
+CF_EXPORT
+void *CFWriteStreamGetInfoPointer(CFWriteStreamRef stream);
+
+// Returns the client info pointer currently set on the stream. These should probably be made public one day.
+CF_EXPORT
+void *_CFReadStreamGetClient(CFReadStreamRef readStream);
+CF_EXPORT
+void *_CFWriteStreamGetClient(CFWriteStreamRef writeStream);
+
+// Returns an array of the runloops and modes on which the stream is currently scheduled
+CF_EXPORT
+CFArrayRef _CFReadStreamGetRunLoopsAndModes(CFReadStreamRef readStream);
+CF_EXPORT
+CFArrayRef _CFWriteStreamGetRunLoopsAndModes(CFWriteStreamRef writeStream);
+
+/* Deprecated versions; here for backwards compatibility. */
+typedef struct {
+ CFIndex version; /* == 1 */
+ void *(*create)(CFReadStreamRef stream, void *info);
+ void (*finalize)(CFReadStreamRef stream, void *info);
+ CFStringRef (*copyDescription)(CFReadStreamRef stream, void *info);
+ Boolean (*open)(CFReadStreamRef stream, CFStreamError *error, Boolean *openComplete, void *info);
+ Boolean (*openCompleted)(CFReadStreamRef stream, CFStreamError *error, void *info);
+ CFIndex (*read)(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info);
+ const UInt8 *(*getBuffer)(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFStreamError *error, Boolean *atEOF, void *info);
+ Boolean (*canRead)(CFReadStreamRef stream, void *info);
+ void (*close)(CFReadStreamRef stream, void *info);
+ CFTypeRef (*copyProperty)(CFReadStreamRef stream, CFStringRef propertyName, void *info);
+ Boolean (*setProperty)(CFReadStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue, void *info);
+ void (*requestEvents)(CFReadStreamRef stream, CFOptionFlags streamEvents, void *info);
+ void (*schedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info);
+ void (*unschedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info);
+} CFReadStreamCallBacksV1;
+
+typedef struct {
+ CFIndex version; /* == 1 */
+ void *(*create)(CFWriteStreamRef stream, void *info);
+ void (*finalize)(CFWriteStreamRef stream, void *info);
+ CFStringRef (*copyDescription)(CFWriteStreamRef stream, void *info);
+ Boolean (*open)(CFWriteStreamRef stream, CFStreamError *error, Boolean *openComplete, void *info);
+ Boolean (*openCompleted)(CFWriteStreamRef stream, CFStreamError *error, void *info);
+ CFIndex (*write)(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, void *info);
+ Boolean (*canWrite)(CFWriteStreamRef stream, void *info);
+ void (*close)(CFWriteStreamRef stream, void *info);
+ CFTypeRef (*copyProperty)(CFWriteStreamRef stream, CFStringRef propertyName, void *info);
+ Boolean (*setProperty)(CFWriteStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue, void *info);
+ void (*requestEvents)(CFWriteStreamRef stream, CFOptionFlags streamEvents, void *info);
+ void (*schedule)(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info);
+ void (*unschedule)(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info);
+} CFWriteStreamCallBacksV1;
+
+typedef struct {
+ CFIndex version; /* == 0 */
+ Boolean (*open)(CFReadStreamRef stream, CFStreamError *error, Boolean *openComplete, void *info);
+ Boolean (*openCompleted)(CFReadStreamRef stream, CFStreamError *error, void *info);
+ CFIndex (*read)(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info);
+ const UInt8 *(*getBuffer)(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFStreamError *error, Boolean *atEOF, void *info);
+ Boolean (*canRead)(CFReadStreamRef stream, void *info);
+ void (*close)(CFReadStreamRef stream, void *info);
+ CFTypeRef (*copyProperty)(CFReadStreamRef stream, CFStringRef propertyName, void *info);
+ void (*schedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info);
+ void (*unschedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info);
+} CFReadStreamCallBacksV0;
+
+typedef struct {
+ CFIndex version; /* == 0 */
+ Boolean (*open)(CFWriteStreamRef stream, CFStreamError *error, Boolean *openComplete, void *info);
+ Boolean (*openCompleted)(CFWriteStreamRef stream, CFStreamError *error, void *info);
+ CFIndex (*write)(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, void *info);
+ Boolean (*canWrite)(CFWriteStreamRef stream, void *info);
+ void (*close)(CFWriteStreamRef stream, void *info);
+ CFTypeRef (*copyProperty)(CFWriteStreamRef stream, CFStringRef propertyName, void *info);
+ void (*schedule)(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info);
+ void (*unschedule)(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info);
+} CFWriteStreamCallBacksV0;
+
+CF_EXTERN_C_END
+
+#endif /* ! __COREFOUNDATION_CFSTREAMABSTRACT__ */
diff --git a/chromium/third_party/crashpad/crashpad/third_party/apple_cf/README.crashpad b/chromium/third_party/crashpad/crashpad/third_party/apple_cf/README.crashpad
new file mode 100644
index 00000000000..b6c6cfad90b
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/third_party/apple_cf/README.crashpad
@@ -0,0 +1,15 @@
+Name: Apple CF-Lite
+Short Name: CF
+URL: https://opensource.apple.com/source/CF/
+URL: https://opensource.apple.com/tarballs/CF/
+Version: 550.43 (from Mac OS X 10.6.8)
+License: APSL 2.0
+License File: APPLE_LICENSE
+Security Critical: no
+
+Description:
+CF-Lite is an open-source version of the CoreFoundation framework. This
+contains non-public but stable header files.
+
+Local Modifications:
+ - Only CFStreamAbstract.h is included.
diff --git a/chromium/third_party/crashpad/crashpad/third_party/getopt/LICENSE b/chromium/third_party/crashpad/crashpad/third_party/getopt/LICENSE
new file mode 100644
index 00000000000..4444b1201e3
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/third_party/getopt/LICENSE
@@ -0,0 +1,5 @@
+Copyright (C) 1997 Gregory Pietsch
+
+[These files] are hereby placed in the public domain without restrictions. Just
+give the author credit, don't claim you wrote it or prevent anyone else from
+using it.
diff --git a/chromium/third_party/crashpad/crashpad/third_party/getopt/README.crashpad b/chromium/third_party/crashpad/crashpad/third_party/getopt/README.crashpad
new file mode 100644
index 00000000000..35d50e28046
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/third_party/getopt/README.crashpad
@@ -0,0 +1,14 @@
+Name: Gregory Pietsch getopt
+Short Name: getopt
+URL: https://sourceware.org/ml/newlib/2005/msg00758.html
+License: Public domain
+License File: LICENSE
+Security Critical: no
+
+Description:
+A public domain implementation of getopt.
+
+Local Modifications:
+- Minor compilation fixes applied for Windows.
+- Add copy of copyright (Public domain) to the top of both files for Chromium's
+ checklicenses step.
diff --git a/chromium/third_party/crashpad/crashpad/third_party/getopt/getopt.c b/chromium/third_party/crashpad/crashpad/third_party/getopt/getopt.c
new file mode 100644
index 00000000000..d63492f17f0
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/third_party/getopt/getopt.c
@@ -0,0 +1,418 @@
+/*
+Copyright (C) 1997 Gregory Pietsch
+
+[These files] are hereby placed in the public domain without restrictions. Just
+give the author credit, don't claim you wrote it or prevent anyone else from
+using it.
+*/
+
+/****************************************************************************
+
+getopt.c - Read command line options
+
+AUTHOR: Gregory Pietsch
+CREATED Fri Jan 10 21:13:05 1997
+
+DESCRIPTION:
+
+The getopt() function parses the command line arguments. Its arguments argc
+and argv are the argument count and array as passed to the main() function
+on program invocation. The argument optstring is a list of available option
+characters. If such a character is followed by a colon (`:'), the option
+takes an argument, which is placed in optarg. If such a character is
+followed by two colons, the option takes an optional argument, which is
+placed in optarg. If the option does not take an argument, optarg is NULL.
+
+The external variable optind is the index of the next array element of argv
+to be processed; it communicates from one call to the next which element to
+process.
+
+The getopt_long() function works like getopt() except that it also accepts
+long options started by two dashes `--'. If these take values, it is either
+in the form
+
+--arg=value
+
+ or
+
+--arg value
+
+It takes the additional arguments longopts which is a pointer to the first
+element of an array of type GETOPT_LONG_OPTION_T. The last element of the
+array has to be filled with NULL for the name field.
+
+The longind pointer points to the index of the current long option relative
+to longopts if it is non-NULL.
+
+The getopt() function returns the option character if the option was found
+successfully, `:' if there was a missing parameter for one of the options,
+`?' for an unknown option character, and EOF for the end of the option list.
+
+The getopt_long() function's return value is described in the header file.
+
+The function getopt_long_only() is identical to getopt_long(), except that a
+plus sign `+' can introduce long options as well as `--'.
+
+The following describes how to deal with options that follow non-option
+argv-elements.
+
+If the caller did not specify anything, the default is REQUIRE_ORDER if the
+environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+REQUIRE_ORDER means don't recognize them as options; stop option processing
+when the first non-option is seen. This is what Unix does. This mode of
+operation is selected by either setting the environment variable
+POSIXLY_CORRECT, or using `+' as the first character of the optstring
+parameter.
+
+PERMUTE is the default. We permute the contents of ARGV as we scan, so that
+eventually all the non-options are at the end. This allows options to be
+given in any order, even with programs that were not written to expect this.
+
+RETURN_IN_ORDER is an option available to programs that were written to
+expect options and other argv-elements in any order and that care about the
+ordering of the two. We describe each non-option argv-element as if it were
+the argument of an option with character code 1. Using `-' as the first
+character of the optstring parameter selects this mode of operation.
+
+The special argument `--' forces an end of option-scanning regardless of the
+value of ordering. In the case of RETURN_IN_ORDER, only `--' can cause
+getopt() and friends to return EOF with optind != argc.
+
+COPYRIGHT NOTICE AND DISCLAIMER:
+
+Copyright (C) 1997 Gregory Pietsch
+
+This file and the accompanying getopt.h header file are hereby placed in the
+public domain without restrictions. Just give the author credit, don't
+claim you wrote it or prevent anyone else from using it.
+
+Gregory Pietsch's current e-mail address:
+gpietsch@comcast.net
+****************************************************************************/
+
+/* include files */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef GETOPT_H
+#include "getopt.h"
+#endif
+
+/* macros */
+
+/* types */
+typedef enum GETOPT_ORDERING_T
+{
+ PERMUTE,
+ RETURN_IN_ORDER,
+ REQUIRE_ORDER
+} GETOPT_ORDERING_T;
+
+/* globally-defined variables */
+char *optarg = NULL;
+int optind = 0;
+int opterr = 1;
+int optopt = '?';
+
+/* functions */
+
+/* reverse_argv_elements: reverses num elements starting at argv */
+static void
+reverse_argv_elements (char **argv, int num)
+{
+ int i;
+ char *tmp;
+
+ for (i = 0; i < (num >> 1); i++)
+ {
+ tmp = argv[i];
+ argv[i] = argv[num - i - 1];
+ argv[num - i - 1] = tmp;
+ }
+}
+
+/* permute: swap two blocks of argv-elements given their lengths */
+static void
+permute (char **argv, int len1, int len2)
+{
+ reverse_argv_elements (argv, len1);
+ reverse_argv_elements (argv, len1 + len2);
+ reverse_argv_elements (argv, len2);
+}
+
+/* is_option: is this argv-element an option or the end of the option list? */
+static int
+is_option (char *argv_element, int only)
+{
+ return ((argv_element == NULL)
+ || (argv_element[0] == '-') || (only && argv_element[0] == '+'));
+}
+
+/* getopt_internal: the function that does all the dirty work */
+static int
+getopt_internal (int argc, char **argv, char *shortopts,
+ GETOPT_LONG_OPTION_T * longopts, int *longind, int only)
+{
+ GETOPT_ORDERING_T ordering = PERMUTE;
+ static size_t optwhere = 0;
+ size_t permute_from = 0;
+ int num_nonopts = 0;
+ int optindex = 0;
+ size_t match_chars = 0;
+ char *possible_arg = NULL;
+ int longopt_match = -1;
+ int has_arg = -1;
+ char *cp = NULL;
+ int arg_next = 0;
+
+ /* first, deal with silly parameters and easy stuff */
+ if (argc == 0 || argv == NULL || (shortopts == NULL && longopts == NULL))
+ return (optopt = '?');
+ if (optind >= argc || argv[optind] == NULL)
+ return EOF;
+ if (strcmp (argv[optind], "--") == 0)
+ {
+ optind++;
+ return EOF;
+ }
+ /* if this is our first time through */
+ if (optind == 0) {
+ optind = 1;
+ optwhere = 1;
+ }
+
+ /* define ordering */
+ if (shortopts != NULL && (*shortopts == '-' || *shortopts == '+'))
+ {
+ ordering = (*shortopts == '-') ? RETURN_IN_ORDER : REQUIRE_ORDER;
+ shortopts++;
+ }
+ else
+ ordering = (getenv ("POSIXLY_CORRECT") != NULL) ? REQUIRE_ORDER : PERMUTE;
+
+ /*
+ * based on ordering, find our next option, if we're at the beginning of
+ * one
+ */
+ if (optwhere == 1)
+ {
+ switch (ordering)
+ {
+ case PERMUTE:
+ permute_from = optind;
+ num_nonopts = 0;
+ while (!is_option (argv[optind], only))
+ {
+ optind++;
+ num_nonopts++;
+ }
+ if (argv[optind] == NULL)
+ {
+ /* no more options */
+ optind = (int)permute_from;
+ return EOF;
+ }
+ else if (strcmp (argv[optind], "--") == 0)
+ {
+ /* no more options, but have to get `--' out of the way */
+ permute (argv + permute_from, num_nonopts, 1);
+ optind = (int)(permute_from + 1);
+ return EOF;
+ }
+ break;
+ case RETURN_IN_ORDER:
+ if (!is_option (argv[optind], only))
+ {
+ optarg = argv[optind++];
+ return (optopt = 1);
+ }
+ break;
+ case REQUIRE_ORDER:
+ if (!is_option (argv[optind], only))
+ return EOF;
+ break;
+ }
+ }
+ /* we've got an option, so parse it */
+
+ /* first, is it a long option? */
+ if (longopts != NULL
+ && (memcmp (argv[optind], "--", 2) == 0
+ || (only && argv[optind][0] == '+')) && optwhere == 1)
+ {
+ /* handle long options */
+ if (memcmp (argv[optind], "--", 2) == 0)
+ optwhere = 2;
+ longopt_match = -1;
+ possible_arg = strchr (argv[optind] + optwhere, '=');
+ if (possible_arg == NULL)
+ {
+ /* no =, so next argv might be arg */
+ match_chars = strlen (argv[optind]);
+ possible_arg = argv[optind] + match_chars;
+ match_chars = match_chars - optwhere;
+ }
+ else
+ match_chars = (possible_arg - argv[optind]) - optwhere;
+ for (optindex = 0; longopts[optindex].name != NULL; optindex++)
+ {
+ if (memcmp (argv[optind] + optwhere,
+ longopts[optindex].name, match_chars) == 0)
+ {
+ /* do we have an exact match? */
+ if (match_chars == strlen (longopts[optindex].name))
+ {
+ longopt_match = optindex;
+ break;
+ }
+ /* do any characters match? */
+ else
+ {
+ if (longopt_match < 0)
+ longopt_match = optindex;
+ else
+ {
+ /* we have ambiguous options */
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' is ambiguous "
+ "(could be `--%s' or `--%s')\n",
+ argv[0],
+ argv[optind],
+ longopts[longopt_match].name,
+ longopts[optindex].name);
+ return (optopt = '?');
+ }
+ }
+ }
+ }
+ if (longopt_match >= 0)
+ has_arg = longopts[longopt_match].has_arg;
+ }
+ /* if we didn't find a long option, is it a short option? */
+ if (longopt_match < 0 && shortopts != NULL)
+ {
+ cp = strchr (shortopts, argv[optind][optwhere]);
+ if (cp == NULL)
+ {
+ /* couldn't find option in shortopts */
+ if (opterr)
+ fprintf (stderr,
+ "%s: invalid option -- `-%c'\n",
+ argv[0], argv[optind][optwhere]);
+ optwhere++;
+ if (argv[optind][optwhere] == '\0')
+ {
+ optind++;
+ optwhere = 1;
+ }
+ return (optopt = '?');
+ }
+ has_arg = ((cp[1] == ':')
+ ? ((cp[2] == ':') ? OPTIONAL_ARG : required_argument) : no_argument);
+ possible_arg = argv[optind] + optwhere + 1;
+ optopt = *cp;
+ }
+ /* get argument and reset optwhere */
+ arg_next = 0;
+ switch (has_arg)
+ {
+ case OPTIONAL_ARG:
+ if (*possible_arg == '=')
+ possible_arg++;
+ if (*possible_arg != '\0')
+ {
+ optarg = possible_arg;
+ optwhere = 1;
+ }
+ else
+ optarg = NULL;
+ break;
+ case required_argument:
+ if (*possible_arg == '=')
+ possible_arg++;
+ if (*possible_arg != '\0')
+ {
+ optarg = possible_arg;
+ optwhere = 1;
+ }
+ else if (optind + 1 >= argc)
+ {
+ if (opterr)
+ {
+ fprintf (stderr, "%s: argument required for option `", argv[0]);
+ if (longopt_match >= 0)
+ fprintf (stderr, "--%s'\n", longopts[longopt_match].name);
+ else
+ fprintf (stderr, "-%c'\n", *cp);
+ }
+ optind++;
+ return (optopt = ':');
+ }
+ else
+ {
+ optarg = argv[optind + 1];
+ arg_next = 1;
+ optwhere = 1;
+ }
+ break;
+ case no_argument:
+ if (longopt_match < 0)
+ {
+ optwhere++;
+ if (argv[optind][optwhere] == '\0')
+ optwhere = 1;
+ }
+ else
+ optwhere = 1;
+ optarg = NULL;
+ break;
+ }
+
+ /* do we have to permute or otherwise modify optind? */
+ if (ordering == PERMUTE && optwhere == 1 && num_nonopts != 0)
+ {
+ permute (argv + permute_from, num_nonopts, 1 + arg_next);
+ optind = (int)(permute_from + 1 + arg_next);
+ }
+ else if (optwhere == 1)
+ optind = optind + 1 + arg_next;
+
+ /* finally return */
+ if (longopt_match >= 0)
+ {
+ if (longind != NULL)
+ *longind = longopt_match;
+ if (longopts[longopt_match].flag != NULL)
+ {
+ *(longopts[longopt_match].flag) = longopts[longopt_match].val;
+ return 0;
+ }
+ else
+ return longopts[longopt_match].val;
+ }
+ else
+ return optopt;
+}
+
+int
+getopt (int argc, char **argv, char *optstring)
+{
+ return getopt_internal (argc, argv, optstring, NULL, NULL, 0);
+}
+
+int
+getopt_long (int argc, char **argv, const char *shortopts,
+ const GETOPT_LONG_OPTION_T * longopts, int *longind)
+{
+ return getopt_internal (argc, argv, (char*)shortopts, (GETOPT_LONG_OPTION_T*)longopts, longind, 0);
+}
+
+int
+getopt_long_only (int argc, char **argv, const char *shortopts,
+ const GETOPT_LONG_OPTION_T * longopts, int *longind)
+{
+ return getopt_internal (argc, argv, (char*)shortopts, (GETOPT_LONG_OPTION_T*)longopts, longind, 1);
+}
+
+/* end of file GETOPT.C */
diff --git a/chromium/third_party/crashpad/crashpad/third_party/getopt/getopt.gyp b/chromium/third_party/crashpad/crashpad/third_party/getopt/getopt.gyp
new file mode 100644
index 00000000000..b7a23269029
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/third_party/getopt/getopt.gyp
@@ -0,0 +1,35 @@
+# Copyright 2015 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'includes': [
+ '../../build/crashpad.gypi',
+ ],
+ 'conditions': [
+ ['OS=="win"', {
+ 'targets': [
+ {
+ 'target_name': 'getopt',
+ 'type': 'static_library',
+ 'sources': [
+ 'getopt.c',
+ 'getopt.h',
+ ],
+ },
+ ],
+ }, {
+ 'targets': []
+ }]
+ ],
+}
diff --git a/chromium/third_party/crashpad/crashpad/third_party/getopt/getopt.h b/chromium/third_party/crashpad/crashpad/third_party/getopt/getopt.h
new file mode 100644
index 00000000000..dea49585119
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/third_party/getopt/getopt.h
@@ -0,0 +1,63 @@
+/*
+Copyright (C) 1997 Gregory Pietsch
+
+[These files] are hereby placed in the public domain without restrictions. Just
+give the author credit, don't claim you wrote it or prevent anyone else from
+using it.
+*/
+
+#ifndef GETOPT_H
+#define GETOPT_H
+
+/* include files needed by this include file */
+
+/* macros defined by this include file */
+#define no_argument 0
+#define required_argument 1
+#define OPTIONAL_ARG 2
+
+/* types defined by this include file */
+
+/* GETOPT_LONG_OPTION_T: The type of long option */
+typedef struct GETOPT_LONG_OPTION_T
+{
+ const char *name; /* the name of the long option */
+ int has_arg; /* one of the above macros */
+ int *flag; /* determines if getopt_long() returns a
+ * value for a long option; if it is
+ * non-NULL, 0 is returned as a function
+ * value and the value of val is stored in
+ * the area pointed to by flag. Otherwise,
+ * val is returned. */
+ int val; /* determines the value to return if flag is
+ * NULL. */
+} GETOPT_LONG_OPTION_T;
+
+typedef GETOPT_LONG_OPTION_T option;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ /* externally-defined variables */
+ extern char *optarg;
+ extern int optind;
+ extern int opterr;
+ extern int optopt;
+
+ /* function prototypes */
+ int getopt (int argc, char **argv, char *optstring);
+ int getopt_long (int argc, char **argv, const char *shortopts,
+ const GETOPT_LONG_OPTION_T * longopts, int *longind);
+ int getopt_long_only (int argc, char **argv, const char *shortopts,
+ const GETOPT_LONG_OPTION_T * longopts, int *longind);
+
+#ifdef __cplusplus
+};
+
+#endif
+
+#endif /* GETOPT_H */
+
+/* END OF FILE getopt.h */
diff --git a/chromium/third_party/crashpad/crashpad/third_party/gmock/README.crashpad b/chromium/third_party/crashpad/crashpad/third_party/gmock/README.crashpad
new file mode 100644
index 00000000000..783f942bfe2
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/third_party/gmock/README.crashpad
@@ -0,0 +1,14 @@
+Name: Google C++ Mocking Framework (googlemock)
+Short Name: gmock
+URL: https://googlemock.googlecode.com/
+Revision: See DEPS
+License: BSD 3-clause
+License File: gmock/LICENSE
+Security Critical: no
+
+Description:
+Google C++ Mocking Framework (or Google Mock for short) is a library for
+writing and using C++ mock classes.
+
+Local Modifications:
+None
diff --git a/chromium/third_party/crashpad/crashpad/third_party/gmock/gmock.gyp b/chromium/third_party/crashpad/crashpad/third_party/gmock/gmock.gyp
new file mode 100644
index 00000000000..22c22b143f4
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/third_party/gmock/gmock.gyp
@@ -0,0 +1,220 @@
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'includes': [
+ '../../build/crashpad_in_chromium.gypi',
+ ],
+ 'conditions': [
+ ['crashpad_in_chromium==0', {
+ 'targets': [
+ {
+ 'target_name': 'gmock',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../gtest/gtest.gyp:gtest',
+ ],
+ 'include_dirs': [
+ 'gmock',
+ 'gmock/include',
+ ],
+ 'sources': [
+ 'gmock/include/gmock/gmock-actions.h',
+ 'gmock/include/gmock/gmock-cardinalities.h',
+ 'gmock/include/gmock/gmock-generated-actions.h',
+ 'gmock/include/gmock/gmock-generated-function-mockers.h',
+ 'gmock/include/gmock/gmock-generated-matchers.h',
+ 'gmock/include/gmock/gmock-generated-nice-strict.h',
+ 'gmock/include/gmock/gmock-matchers.h',
+ 'gmock/include/gmock/gmock-more-actions.h',
+ 'gmock/include/gmock/gmock-more-matchers.h',
+ 'gmock/include/gmock/gmock-spec-builders.h',
+ 'gmock/include/gmock/gmock.h',
+ 'gmock/include/gmock/internal/gmock-generated-internal-utils.h',
+ 'gmock/include/gmock/internal/gmock-internal-utils.h',
+ 'gmock/include/gmock/internal/gmock-port.h',
+ 'gmock/src/gmock-all.cc',
+ 'gmock/src/gmock-cardinalities.cc',
+ 'gmock/src/gmock-internal-utils.cc',
+ 'gmock/src/gmock-matchers.cc',
+ 'gmock/src/gmock-spec-builders.cc',
+ 'gmock/src/gmock.cc',
+ ],
+ 'sources!': [
+ 'gmock/src/gmock-all.cc',
+ ],
+
+ # gmock relies heavily on objects with static storage duration.
+ 'xcode_settings': {
+ 'WARNING_CFLAGS!': [
+ '-Wexit-time-destructors',
+ ],
+ },
+ 'cflags!': [
+ '-Wexit-time-destructors',
+ ],
+
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ 'gmock/include',
+ ],
+ 'conditions': [
+ ['clang!=0', {
+ # The MOCK_METHODn() macros do not specify “override”, which
+ # triggers this warning in users: “error: 'Method' overrides a
+ # member function but is not marked 'override'
+ # [-Werror,-Winconsistent-missing-override]”. Suppress these
+ # warnings, and add -Wno-unknown-warning-option because only
+ # recent versions of clang (trunk r220703 and later, version
+ # 3.6 and later) recognize it.
+ 'conditions': [
+ ['OS=="mac"', {
+ 'xcode_settings': {
+ 'WARNING_CFLAGS': [
+ '-Wno-inconsistent-missing-override',
+ '-Wno-unknown-warning-option',
+ ],
+ },
+ }],
+ ['OS=="linux"', {
+ 'cflags': [
+ '-Wno-inconsistent-missing-override',
+ '-Wno-unknown-warning-option',
+ ],
+ }],
+ ],
+ }],
+ ],
+ },
+ 'export_dependent_settings': [
+ '../gtest/gtest.gyp:gtest',
+ ],
+ },
+ {
+ 'target_name': 'gmock_main',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'gmock',
+ '../gtest/gtest.gyp:gtest',
+ ],
+ 'sources': [
+ 'gmock/src/gmock_main.cc',
+ ],
+ },
+ {
+ 'target_name': 'gmock_test_executable',
+ 'type': 'none',
+ 'dependencies': [
+ 'gmock',
+ '../gtest/gtest.gyp:gtest',
+ ],
+ 'direct_dependent_settings': {
+ 'type': 'executable',
+ 'include_dirs': [
+ 'gmock',
+ ],
+ },
+ 'export_dependent_settings': [
+ 'gmock',
+ '../gtest/gtest.gyp:gtest',
+ ],
+ },
+ {
+ 'target_name': 'gmock_all_test',
+ 'dependencies': [
+ 'gmock_test_executable',
+ 'gmock_main',
+ ],
+ 'sources': [
+ 'gmock/test/gmock-actions_test.cc',
+ 'gmock/test/gmock-cardinalities_test.cc',
+ 'gmock/test/gmock-generated-actions_test.cc',
+ 'gmock/test/gmock-generated-function-mockers_test.cc',
+ 'gmock/test/gmock-generated-internal-utils_test.cc',
+ 'gmock/test/gmock-generated-matchers_test.cc',
+ 'gmock/test/gmock-internal-utils_test.cc',
+ 'gmock/test/gmock-matchers_test.cc',
+ 'gmock/test/gmock-more-actions_test.cc',
+ 'gmock/test/gmock-nice-strict_test.cc',
+ 'gmock/test/gmock-port_test.cc',
+ 'gmock/test/gmock_test.cc',
+ ],
+ },
+ {
+ 'target_name': 'gmock_link_test',
+ 'dependencies': [
+ 'gmock_test_executable',
+ 'gmock_main',
+ ],
+ 'sources': [
+ 'gmock/test/gmock_link_test.cc',
+ 'gmock/test/gmock_link_test.h',
+ 'gmock/test/gmock_link2_test.cc',
+ ],
+ },
+ {
+ 'target_name': 'gmock_spec_builders_test',
+ 'dependencies': [
+ 'gmock_test_executable',
+ ],
+ 'sources': [
+ 'gmock/test/gmock-spec-builders_test.cc',
+ ],
+ },
+ {
+ 'target_name': 'gmock_stress_test',
+ 'dependencies': [
+ 'gmock_test_executable',
+ ],
+ 'sources': [
+ 'gmock/test/gmock_stress_test.cc',
+ ],
+ },
+ {
+ 'target_name': 'gmock_all_tests',
+ 'type': 'none',
+ 'dependencies': [
+ 'gmock_all_test',
+ 'gmock_link_test',
+ 'gmock_spec_builders_test',
+ 'gmock_stress_test',
+ ],
+ },
+ ],
+ }, { # else: crashpad_in_chromium!=0
+ 'targets': [
+ {
+ 'target_name': 'gmock',
+ 'type': 'none',
+ 'dependencies': [
+ '../../../../../testing/gmock.gyp:gmock',
+ ],
+ 'export_dependent_settings': [
+ '../../../../../testing/gmock.gyp:gmock',
+ ],
+ },
+ {
+ 'target_name': 'gmock_main',
+ 'type': 'none',
+ 'dependencies': [
+ '../../../../../testing/gmock.gyp:gmock_main',
+ ],
+ 'export_dependent_settings': [
+ '../../../../../testing/gmock.gyp:gmock_main',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/chromium/third_party/crashpad/crashpad/third_party/gtest/README.crashpad b/chromium/third_party/crashpad/crashpad/third_party/gtest/README.crashpad
new file mode 100644
index 00000000000..5bc23a0db37
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/third_party/gtest/README.crashpad
@@ -0,0 +1,14 @@
+Name: Google C++ Testing Framework (googletest)
+Short Name: gtest
+URL: https://googletest.googlecode.com/
+Revision: See DEPS
+License: BSD 3-clause
+License File: gtest/LICENSE
+Security Critical: no
+
+Description:
+Google C++ Testing Framework (or Google Test for short) is Google’s framework
+for writing C++ tests on a variety of platforms.
+
+Local Modifications:
+None
diff --git a/chromium/third_party/crashpad/crashpad/third_party/gtest/gtest.gyp b/chromium/third_party/crashpad/crashpad/third_party/gtest/gtest.gyp
new file mode 100644
index 00000000000..b9f2356554f
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/third_party/gtest/gtest.gyp
@@ -0,0 +1,255 @@
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'includes': [
+ '../../build/crashpad_in_chromium.gypi',
+ ],
+ 'conditions': [
+ ['crashpad_in_chromium==0', {
+ 'targets': [
+ {
+ 'target_name': 'gtest',
+ 'type': 'static_library',
+ 'include_dirs': [
+ 'gtest',
+ 'gtest/include',
+ ],
+ 'sources': [
+ 'gtest/include/gtest/gtest-death-test.h',
+ 'gtest/include/gtest/gtest-message.h',
+ 'gtest/include/gtest/gtest-param-test.h',
+ 'gtest/include/gtest/gtest-printers.h',
+ 'gtest/include/gtest/gtest-spi.h',
+ 'gtest/include/gtest/gtest-test-part.h',
+ 'gtest/include/gtest/gtest-typed-test.h',
+ 'gtest/include/gtest/gtest.h',
+ 'gtest/include/gtest/gtest_pred_impl.h',
+ 'gtest/include/gtest/gtest_prod.h',
+ 'gtest/include/gtest/internal/gtest-death-test-internal.h',
+ 'gtest/include/gtest/internal/gtest-filepath.h',
+ 'gtest/include/gtest/internal/gtest-internal.h',
+ 'gtest/include/gtest/internal/gtest-linked_ptr.h',
+ 'gtest/include/gtest/internal/gtest-param-util-generated.h',
+ 'gtest/include/gtest/internal/gtest-param-util.h',
+ 'gtest/include/gtest/internal/gtest-port.h',
+ 'gtest/include/gtest/internal/gtest-string.h',
+ 'gtest/include/gtest/internal/gtest-tuple.h',
+ 'gtest/include/gtest/internal/gtest-type-util.h',
+ 'gtest/src/gtest.cc',
+ 'gtest/src/gtest-death-test.cc',
+ 'gtest/src/gtest-filepath.cc',
+ 'gtest/src/gtest-port.cc',
+ 'gtest/src/gtest-printers.cc',
+ 'gtest/src/gtest-test-part.cc',
+ 'gtest/src/gtest-typed-test.cc',
+ ],
+ 'sources!': [
+ 'gtest/src/gtest-all.cc',
+ ],
+
+ # gtest relies heavily on objects with static storage duration.
+ 'xcode_settings': {
+ 'WARNING_CFLAGS!': [
+ '-Wexit-time-destructors',
+ ],
+ },
+ 'cflags!': [
+ '-Wexit-time-destructors',
+ ],
+
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ 'gtest/include',
+ ],
+ },
+ },
+ {
+ 'target_name': 'gtest_main',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'gtest',
+ ],
+ 'sources': [
+ 'gtest/src/gtest_main.cc',
+ ],
+ },
+ {
+ 'target_name': 'gtest_test_executable',
+ 'type': 'none',
+ 'dependencies': [
+ 'gtest',
+ ],
+ 'direct_dependent_settings': {
+ 'type': 'executable',
+ 'include_dirs': [
+ 'gtest',
+ ],
+ },
+ 'export_dependent_settings': [
+ 'gtest',
+ ],
+ },
+ {
+ 'target_name': 'gtest_all_test',
+ 'dependencies': [
+ 'gtest_test_executable',
+ 'gtest_main',
+ ],
+ 'sources': [
+ 'gtest/test/gtest-death-test_test.cc',
+ 'gtest/test/gtest-filepath_test.cc',
+ 'gtest/test/gtest-linked_ptr_test.cc',
+ 'gtest/test/gtest-message_test.cc',
+ 'gtest/test/gtest-options_test.cc',
+ 'gtest/test/gtest-port_test.cc',
+ 'gtest/test/gtest-printers_test.cc',
+ 'gtest/test/gtest-test-part_test.cc',
+ 'gtest/test/gtest-typed-test_test.cc',
+ 'gtest/test/gtest-typed-test_test.h',
+ 'gtest/test/gtest-typed-test2_test.cc',
+ 'gtest/test/gtest_main_unittest.cc',
+ 'gtest/test/gtest_pred_impl_unittest.cc',
+ 'gtest/test/gtest_prod_test.cc',
+ 'gtest/test/gtest_unittest.cc',
+ 'gtest/test/production.cc',
+ 'gtest/test/production.h',
+ ],
+ },
+ {
+ 'target_name': 'gtest_environment_test',
+ 'dependencies': [
+ 'gtest_test_executable',
+ ],
+ 'sources': [
+ 'gtest/test/gtest_environment_test.cc',
+ ],
+ },
+ {
+ 'target_name': 'gtest_listener_test',
+ 'dependencies': [
+ 'gtest_test_executable',
+ ],
+ 'sources': [
+ 'gtest/test/gtest-listener_test.cc',
+ ],
+ },
+ {
+ 'target_name': 'gtest_no_test',
+ 'dependencies': [
+ 'gtest_test_executable',
+ ],
+ 'sources': [
+ 'gtest/test/gtest_no_test_unittest.cc',
+ ],
+ },
+ {
+ 'target_name': 'gtest_param_test',
+ 'dependencies': [
+ 'gtest_test_executable',
+ ],
+ 'sources': [
+ 'gtest/test/gtest-param-test_test.cc',
+ 'gtest/test/gtest-param-test_test.h',
+ 'gtest/test/gtest-param-test2_test.cc',
+ ],
+ },
+ {
+ 'target_name': 'gtest_premature_exit_test',
+ 'dependencies': [
+ 'gtest_test_executable',
+ ],
+ 'sources': [
+ 'gtest/test/gtest_premature_exit_test.cc',
+ ],
+ },
+ {
+ 'target_name': 'gtest_repeat_test',
+ 'dependencies': [
+ 'gtest_test_executable',
+ ],
+ 'sources': [
+ 'gtest/test/gtest_repeat_test.cc',
+ ],
+ },
+ {
+ 'target_name': 'gtest_sole_header_test',
+ 'dependencies': [
+ 'gtest_test_executable',
+ 'gtest_main',
+ ],
+ 'sources': [
+ 'gtest/test/gtest_sole_header_test.cc',
+ ],
+ },
+ {
+ 'target_name': 'gtest_stress_test',
+ 'dependencies': [
+ 'gtest_test_executable',
+ ],
+ 'sources': [
+ 'gtest/test/gtest_stress_test.cc',
+ ],
+ },
+ {
+ 'target_name': 'gtest_unittest_api_test',
+ 'dependencies': [
+ 'gtest_test_executable',
+ ],
+ 'sources': [
+ 'gtest/test/gtest-unittest-api_test.cc',
+ ],
+ },
+ {
+ 'target_name': 'gtest_all_tests',
+ 'type': 'none',
+ 'dependencies': [
+ 'gtest_all_test',
+ 'gtest_environment_test',
+ 'gtest_no_test',
+ 'gtest_param_test',
+ 'gtest_premature_exit_test',
+ 'gtest_repeat_test',
+ 'gtest_sole_header_test',
+ 'gtest_stress_test',
+ 'gtest_unittest_api_test',
+ ],
+ },
+ ],
+ }, { # else: crashpad_in_chromium!=0
+ 'targets': [
+ {
+ 'target_name': 'gtest',
+ 'type': 'none',
+ 'dependencies': [
+ '../../../../../testing/gtest.gyp:gtest',
+ ],
+ 'export_dependent_settings': [
+ '../../../../../testing/gtest.gyp:gtest',
+ ],
+ },
+ {
+ 'target_name': 'gtest_main',
+ 'type': 'none',
+ 'dependencies': [
+ '../../../../../testing/gtest.gyp:gtest_main',
+ ],
+ 'export_dependent_settings': [
+ '../../../../../testing/gtest.gyp:gtest_main',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/chromium/third_party/crashpad/crashpad/third_party/gyp/README.crashpad b/chromium/third_party/crashpad/crashpad/third_party/gyp/README.crashpad
new file mode 100644
index 00000000000..6121d7c9682
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/third_party/gyp/README.crashpad
@@ -0,0 +1,13 @@
+Name: GYP (Generate Your Projects)
+Short Name: gyp
+URL: https://gyp.googlecode.com/
+Revision: See DEPS
+License: BSD 3-clause
+License File: gyp/LICENSE
+Security Critical: no
+
+Description:
+GYP is used to generate build files.
+
+Local Modifications:
+None
diff --git a/chromium/third_party/crashpad/crashpad/third_party/mini_chromium/README.crashpad b/chromium/third_party/crashpad/crashpad/third_party/mini_chromium/README.crashpad
new file mode 100644
index 00000000000..1fa3c4d9dfe
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/third_party/mini_chromium/README.crashpad
@@ -0,0 +1,17 @@
+Name: mini_chromium
+Short Name: mini_chromium
+URL: https://chromium.googlesource.com/chromium/mini_chromium/
+Revision: See DEPS
+License: BSD 3-clause
+License File: mini_chromium/LICENSE
+Security Critical: yes
+
+Description:
+mini_chromium is a small collection of useful low-level (“base”) routines from
+the Chromium open-source project at http://www.chromium.org/. Chromium is
+large, sprawling, full of dependencies, and a web browser. mini_chromium is
+small, self-contained, and a library. mini_chromium is especially useful as a
+dependency of other code that wishes to use Chromium’s base routines.
+
+Local Modifications:
+None
diff --git a/chromium/third_party/crashpad/crashpad/third_party/mini_chromium/mini_chromium.gyp b/chromium/third_party/crashpad/crashpad/third_party/mini_chromium/mini_chromium.gyp
new file mode 100644
index 00000000000..29f12635ab2
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/third_party/mini_chromium/mini_chromium.gyp
@@ -0,0 +1,46 @@
+# Copyright 2015 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'includes': [
+ '../../build/crashpad_in_chromium.gypi',
+ ],
+ 'targets': [
+ {
+ # To support both Crashpad’s standalone build and its in-Chromium build,
+ # Crashpad code depending on base should do so through this shim, which
+ # will either get base from mini_chromium or Chromium depending on the
+ # build type.
+ 'target_name': 'base',
+ 'type': 'none',
+ 'conditions': [
+ ['crashpad_in_chromium==0', {
+ 'dependencies': [
+ 'mini_chromium/base/base.gyp:base',
+ ],
+ 'export_dependent_settings': [
+ 'mini_chromium/base/base.gyp:base',
+ ],
+ }, { # else: crashpad_in_chromium!=0
+ 'dependencies': [
+ '../../../../../base/base.gyp:base',
+ ],
+ 'export_dependent_settings': [
+ '../../../../../base/base.gyp:base',
+ ],
+ }],
+ ],
+ },
+ ],
+}
diff --git a/chromium/third_party/crashpad/crashpad/tools/crashpad_database_util.ad b/chromium/third_party/crashpad/crashpad/tools/crashpad_database_util.ad
new file mode 100644
index 00000000000..b4b6f2cfeb9
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/tools/crashpad_database_util.ad
@@ -0,0 +1,159 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+:doctype: manpage
+
+= crashpad_database_util(1)
+
+== Name
+
+crashpad_database_util - Operate on Crashpad crash report databases
+
+== Synopsis
+
+[verse]
+*crashpad_database_util* ['OPTION…']
+
+== Description
+
+Operates on Crashpad crash report databases. The database’s settings can be
+queried and modified, and information about crash reports stored in the
+database can be displayed.
+
+When this program is requested to both show and set information in a single
+invocation, all “show” operations will be completed prior to beginning any “set”
+operation.
+
+Programs that use the Crashpad client library directly will not normally use
+this tool, but may use the database through the programmatic interfaces in the
+client library. This tool exists to allow developers to manipulate a Crashpad
+database.
+
+== Options
+*-d*, *--database*='PATH'::
+Use 'PATH' as the path to the Crashpad crash report database. This option is
+required. If the database does not exist, it will be created, provided that the
+parent directory of 'PATH' exists.
+
+*--show-client-id*::
+Show the client ID stored in the database’s settings. The client ID is formatted
+as a UUID. The client ID is set when the database is created.
+man_link:crashpad_handler[8] retrieves the client ID and stores it in crash
+reports as they are written.
+
+*--show-uploads-enabled*::
+Show the status of the uploads-enabled bit stored in the database’s settings.
+man_link:crashpad_handler[8] does not upload reports when this bit is false.
+This bit is false when a database is created, and is under an application’s
+control via the Crashpad client library interface.
++
+See also *--set-uploads-enabled*.
+
+*--show-last-upload-attempt-time*::
+Show the last-upload-attempt time stored in the database’s settings. This value
+is +0+, meaning “never,” when the database is created.
+man_link:crashpad_handler[8] consults this value before attempting an upload to
+implement its rate-limiting behavior. The database updates this value whenever
+an upload is attempted.
++
+See also *--set-last-upload-attempt-time*.
+
+*--show-pending-reports*::
+Show reports eligible for upload.
+
+*--show-completed-reports*::
+Show reports not eligible for upload. A report is moved from the “pending” state
+to the “completed” state by man_link:crashpad_handler[8]. This may happen when a
+report is successfully uploaded, when a report is not uploaded because uploads
+are disabled, or when a report upload attempt fails and will not be retried.
+
+*--show-all-report-info*::
+With *--show-pending-reports* or *--show-completed-reports*, show all metadata
+for each report displayed. Without this option, only report IDs will be shown.
+
+*--show-report*='UUID'::
+Show a report from the database looked up by its identifier, 'UUID', which must
+be formatted in string representation per RFC 4122 §3. All metadata for each
+report found via a *--show-report* option will be shown. If 'UUID' is not found,
+the string +"not found"+ will be printed. If this program is only requested to
+show a single report and it is not found, it will treat this as a failure for
+the purposes of determining its exit status. This option may appear multiple
+times.
+
+*--set-report-uploads-enabled*='BOOL'::
+Enable or disable report upload in the database’s settings. 'BOOL' is a string
+representation of a boolean value, such as +"0"+ or +"true"+.
++
+See also *--show-uploads-enabled*.
+
+*--set-last-upload-attempt-time*='TIME'::
+Set the last-upload-attempt time in the database’s settings. 'TIME' is a string
+representation of a time, which may be in 'yyyy-mm-dd hh:mm:ss' format, a
+numeric +time_t+ value, or the special string +"never"+.
++
+See also *--show-last-upload-attempt-time*.
+
+*--new-report*='PATH'::
+Submit a new report located at 'PATH' to the database. The new report will be in
+the “pending” state. The UUID assigned to the new report will be printed. This
+option may appear multiple times.
+
+*--utc*::
+When showing times, do so in UTC as opposed to the local time zone. When setting
+times, interpret ambiguous time strings in UTC as opposed to the local time
+zone.
+
+*--help*::
+Display help and exit.
+
+*--version*::
+Output version information and exit.
+
+== Examples
+
+Shows all crash reports in a crash report database that are in the “completed”
+state.
+[subs="quotes"]
+----
+$ *crashpad_database_util --database /tmp/crashpad_database \
+ --show-completed-reports*
+23f9512b-63e1-4ead-9dcd-e2e21fbccc68
+4bfca440-039f-4bc6-bbd4-6933cef5efd4
+56caeff8-b61a-43b2-832d-9e796e6e4a50
+----
+
+Disables report upload in a crash report database’s settings, and then verifies
+that the change was made.
+[subs="quotes"]
+----
+$ *crashpad_database_util --database /tmp/crashpad_database \
+ --set-uploads-enabled false*
+$ *crashpad_database_util --database /tmp/crashpad_database \
+ --show-uploads-enabled*
+false
+----
+
+== Exit Status
+
+*0*::
+Success.
+
+*1*::
+Failure, with a message printed to the standard error stream.
+
+== See Also
+
+man_link:crashpad_handler[8]
+
+include::../doc/support/man_footer.ad[]
diff --git a/chromium/third_party/crashpad/crashpad/tools/crashpad_database_util.cc b/chromium/third_party/crashpad/crashpad/tools/crashpad_database_util.cc
new file mode 100644
index 00000000000..c06cb5768c5
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/tools/crashpad_database_util.cc
@@ -0,0 +1,582 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "client/crash_report_database.h"
+#include "client/settings.h"
+#include "tools/tool_support.h"
+#include "util/file/file_io.h"
+#include "util/file/file_reader.h"
+#include "util/misc/uuid.h"
+
+namespace crashpad {
+namespace {
+
+void Usage(const base::FilePath& me) {
+ fprintf(stderr,
+"Usage: %" PRFilePath " [OPTION]... PID\n"
+"Operate on Crashpad crash report databases.\n"
+"\n"
+" -d, --database=PATH operate on the crash report database at PATH\n"
+" --show-client-id show the client ID\n"
+" --show-uploads-enabled show whether uploads are enabled\n"
+" --show-last-upload-attempt-time\n"
+" show the last-upload-attempt time\n"
+" --show-pending-reports show reports eligible for upload\n"
+" --show-completed-reports show reports not eligible for upload\n"
+" --show-all-report-info with --show-*-reports, show more information\n"
+" --show-report=UUID show report stored under UUID\n"
+" --set-uploads-enabled=BOOL enable or disable uploads\n"
+" --set-last-upload-attempt-time=TIME\n"
+" set the last-upload-attempt time to TIME\n"
+" --new-report=PATH submit a new report at PATH\n"
+" --utc show and set UTC times instead of local\n"
+" --help display this help and exit\n"
+" --version output version information and exit\n",
+ me.value().c_str());
+ ToolSupport::UsageTail(me);
+}
+
+struct Options {
+ std::vector<UUID> show_reports;
+ std::vector<base::FilePath> new_report_paths;
+ const char* database;
+ const char* set_last_upload_attempt_time_string;
+ time_t set_last_upload_attempt_time;
+ bool show_client_id;
+ bool show_uploads_enabled;
+ bool show_last_upload_attempt_time;
+ bool show_pending_reports;
+ bool show_completed_reports;
+ bool show_all_report_info;
+ bool set_uploads_enabled;
+ bool has_set_uploads_enabled;
+ bool utc;
+};
+
+// Converts |string| to |boolean|, returning true if a conversion could be
+// performed, and false without setting |boolean| if no conversion could be
+// performed. Various string representations of a boolean are recognized
+// case-insensitively.
+bool StringToBool(const char* string, bool* boolean) {
+ const char* const kFalseWords[] = {
+ "0",
+ "false",
+ "no",
+ "off",
+ "disabled",
+ "clear",
+ };
+ const char* const kTrueWords[] = {
+ "1",
+ "true",
+ "yes",
+ "on",
+ "enabled",
+ "set",
+ };
+
+ for (size_t index = 0; index < arraysize(kFalseWords); ++index) {
+ if (base::strcasecmp(string, kFalseWords[index]) == 0) {
+ *boolean = false;
+ return true;
+ }
+ }
+
+ for (size_t index = 0; index < arraysize(kTrueWords); ++index) {
+ if (base::strcasecmp(string, kTrueWords[index]) == 0) {
+ *boolean = true;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Converts |boolean| to a string, either "true" or "false".
+std::string BoolToString(bool boolean) {
+ return std::string(boolean ? "true" : "false");
+}
+
+// Converts |string| to |time|, returning true if a conversion could be
+// performed, and false without setting |boolean| if no conversion could be
+// performed. Various time formats are recognized, including several string
+// representations and a numeric time_t representation. The special string
+// "never" is recognized as |string| and converts to a |time| value of 0. |utc|,
+// when true, causes |string| to be interpreted as a UTC time rather than a
+// local time when the time zone is ambiguous.
+bool StringToTime(const char* string, time_t* time, bool utc) {
+ if (base::strcasecmp(string, "never") == 0) {
+ *time = 0;
+ return true;
+ }
+
+ const char* end = string + strlen(string);
+
+ const char* const kFormats[] = {
+ "%Y-%m-%d %H:%M:%S %Z",
+ "%Y-%m-%d %H:%M:%S",
+ "%+",
+ };
+
+ for (size_t index = 0; index < arraysize(kFormats); ++index) {
+ tm time_tm;
+ const char* strptime_result = strptime(string, kFormats[index], &time_tm);
+ if (strptime_result == end) {
+ if (utc) {
+ *time = timegm(&time_tm);
+ } else {
+ *time = mktime(&time_tm);
+ }
+
+ return true;
+ }
+ }
+
+ char* end_result;
+ errno = 0;
+ long long strtoll_result = strtoll(string, &end_result, 0);
+ if (end_result == end && errno == 0 &&
+ base::IsValueInRangeForNumericType<time_t>(strtoll_result)) {
+ *time = strtoll_result;
+ return true;
+ }
+
+ return false;
+}
+
+// Converts |time_tt| to a string, and returns it. |utc| determines whether the
+// converted time will reference local time or UTC. If |time_tt| is 0, the
+// string "never" will be returned as a special case.
+std::string TimeToString(time_t time_tt, bool utc) {
+ if (time_tt == 0) {
+ return std::string("never");
+ }
+
+ tm time_tm;
+ if (utc) {
+ gmtime_r(&time_tt, &time_tm);
+ } else {
+ localtime_r(&time_tt, &time_tm);
+ }
+
+ char string[64];
+ CHECK_NE(
+ strftime(string, arraysize(string), "%Y-%m-%d %H:%M:%S %Z", &time_tm),
+ 0u);
+
+ return std::string(string);
+}
+
+// Shows information about a single |report|. |space_count| is the number of
+// spaces to print before each line that is printed. |utc| determines whether
+// times should be shown in UTC or the local time zone.
+void ShowReport(const CrashReportDatabase::Report& report,
+ size_t space_count,
+ bool utc) {
+ std::string spaces(space_count, ' ');
+
+ printf("%sPath: %" PRFilePath "\n",
+ spaces.c_str(),
+ report.file_path.value().c_str());
+ if (!report.id.empty()) {
+ printf("%sRemote ID: %s\n", spaces.c_str(), report.id.c_str());
+ }
+ printf("%sCreation time: %s\n",
+ spaces.c_str(),
+ TimeToString(report.creation_time, utc).c_str());
+ printf("%sUploaded: %s\n",
+ spaces.c_str(),
+ BoolToString(report.uploaded).c_str());
+ printf("%sLast upload attempt time: %s\n",
+ spaces.c_str(),
+ TimeToString(report.last_upload_attempt_time, utc).c_str());
+ printf("%sUpload attempts: %d\n", spaces.c_str(), report.upload_attempts);
+}
+
+// Shows information about a vector of |reports|. |space_count| is the number of
+// spaces to print before each line that is printed. |options| will be consulted
+// to determine whether to show expanded information
+// (options.show_all_report_info) and what time zone to use when showing
+// expanded information (options.utc).
+void ShowReports(const std::vector<CrashReportDatabase::Report>& reports,
+ size_t space_count,
+ const Options& options) {
+ std::string spaces(space_count, ' ');
+ const char* colon = options.show_all_report_info ? ":" : "";
+
+ for (const CrashReportDatabase::Report& report : reports) {
+ printf("%s%s%s\n", spaces.c_str(), report.uuid.ToString().c_str(), colon);
+ if (options.show_all_report_info) {
+ ShowReport(report, space_count + 2, options.utc);
+ }
+ }
+}
+
+int DatabaseUtilMain(int argc, char* argv[]) {
+ const base::FilePath argv0(
+ ToolSupport::CommandLineArgumentToFilePathStringType(argv[0]));
+ const base::FilePath me(argv0.BaseName());
+
+ enum OptionFlags {
+ // “Short” (single-character) options.
+ kOptionDatabase = 'd',
+
+ // Long options without short equivalents.
+ kOptionLastChar = 255,
+ kOptionShowClientID,
+ kOptionShowUploadsEnabled,
+ kOptionShowLastUploadAttemptTime,
+ kOptionShowPendingReports,
+ kOptionShowCompletedReports,
+ kOptionShowAllReportInfo,
+ kOptionShowReport,
+ kOptionSetUploadsEnabled,
+ kOptionSetLastUploadAttemptTime,
+ kOptionNewReport,
+ kOptionUTC,
+
+ // Standard options.
+ kOptionHelp = -2,
+ kOptionVersion = -3,
+ };
+
+ const option long_options[] = {
+ {"database", required_argument, nullptr, kOptionDatabase},
+ {"show-client-id", no_argument, nullptr, kOptionShowClientID},
+ {"show-uploads-enabled", no_argument, nullptr, kOptionShowUploadsEnabled},
+ {"show-last-upload-attempt-time",
+ no_argument,
+ nullptr,
+ kOptionShowLastUploadAttemptTime},
+ {"show-pending-reports", no_argument, nullptr, kOptionShowPendingReports},
+ {"show-completed-reports",
+ no_argument,
+ nullptr,
+ kOptionShowCompletedReports},
+ {"show-all-report-info", no_argument, nullptr, kOptionShowAllReportInfo},
+ {"show-report", required_argument, nullptr, kOptionShowReport},
+ {"set-uploads-enabled",
+ required_argument,
+ nullptr,
+ kOptionSetUploadsEnabled},
+ {"set-last-upload-attempt-time",
+ required_argument,
+ nullptr,
+ kOptionSetLastUploadAttemptTime},
+ {"new-report", required_argument, nullptr, kOptionNewReport},
+ {"utc", no_argument, nullptr, kOptionUTC},
+ {"help", no_argument, nullptr, kOptionHelp},
+ {"version", no_argument, nullptr, kOptionVersion},
+ {nullptr, 0, nullptr, 0},
+ };
+
+ Options options = {};
+
+ int opt;
+ while ((opt = getopt_long(argc, argv, "d:", long_options, nullptr)) != -1) {
+ switch (opt) {
+ case kOptionDatabase: {
+ options.database = optarg;
+ break;
+ }
+ case kOptionShowClientID: {
+ options.show_client_id = true;
+ break;
+ }
+ case kOptionShowUploadsEnabled: {
+ options.show_uploads_enabled = true;
+ break;
+ }
+ case kOptionShowLastUploadAttemptTime: {
+ options.show_last_upload_attempt_time = true;
+ break;
+ }
+ case kOptionShowPendingReports: {
+ options.show_pending_reports = true;
+ break;
+ }
+ case kOptionShowCompletedReports: {
+ options.show_completed_reports = true;
+ break;
+ }
+ case kOptionShowAllReportInfo: {
+ options.show_all_report_info = true;
+ break;
+ }
+ case kOptionShowReport: {
+ UUID uuid;
+ if (!uuid.InitializeFromString(optarg)) {
+ ToolSupport::UsageHint(me, "--show-report requires a UUID");
+ return EXIT_FAILURE;
+ }
+ options.show_reports.push_back(uuid);
+ break;
+ }
+ case kOptionSetUploadsEnabled: {
+ if (!StringToBool(optarg, &options.set_uploads_enabled)) {
+ ToolSupport::UsageHint(me, "--set-uploads-enabled requires a BOOL");
+ return EXIT_FAILURE;
+ }
+ options.has_set_uploads_enabled = true;
+ break;
+ }
+ case kOptionSetLastUploadAttemptTime: {
+ options.set_last_upload_attempt_time_string = optarg;
+ break;
+ }
+ case kOptionNewReport: {
+ options.new_report_paths.push_back(base::FilePath(
+ ToolSupport::CommandLineArgumentToFilePathStringType(optarg)));
+ break;
+ }
+ case kOptionUTC: {
+ options.utc = true;
+ break;
+ }
+ case kOptionHelp: {
+ Usage(me);
+ return EXIT_SUCCESS;
+ }
+ case kOptionVersion: {
+ ToolSupport::Version(me);
+ return EXIT_SUCCESS;
+ }
+ default: {
+ ToolSupport::UsageHint(me, nullptr);
+ return EXIT_FAILURE;
+ }
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!options.database) {
+ ToolSupport::UsageHint(me, "--database is required");
+ return EXIT_FAILURE;
+ }
+
+ // This conversion couldn’t happen in the option-processing loop above because
+ // it depends on options.utc, which may have been set after
+ // options.set_last_upload_attempt_time_string.
+ if (options.set_last_upload_attempt_time_string) {
+ if (!StringToTime(options.set_last_upload_attempt_time_string,
+ &options.set_last_upload_attempt_time,
+ options.utc)) {
+ ToolSupport::UsageHint(me,
+ "--set-last-upload-attempt-time requires a TIME");
+ return EXIT_FAILURE;
+ }
+ }
+
+ // --new-report is treated as a show operation because it produces output.
+ const size_t show_operations = options.show_client_id +
+ options.show_uploads_enabled +
+ options.show_last_upload_attempt_time +
+ options.show_pending_reports +
+ options.show_completed_reports +
+ options.show_reports.size() +
+ options.new_report_paths.size();
+ const size_t set_operations =
+ options.has_set_uploads_enabled +
+ (options.set_last_upload_attempt_time_string != nullptr);
+
+ if (show_operations + set_operations == 0) {
+ ToolSupport::UsageHint(me, "nothing to do");
+ return EXIT_FAILURE;
+ }
+
+ scoped_ptr<CrashReportDatabase> database(CrashReportDatabase::Initialize(
+ base::FilePath(ToolSupport::CommandLineArgumentToFilePathStringType(
+ options.database))));
+ if (!database) {
+ return EXIT_FAILURE;
+ }
+
+ Settings* settings = database->GetSettings();
+
+ // Handle the “show” options before the “set” options so that when they’re
+ // specified together, the “show” option reflects the initial state.
+
+ if (options.show_client_id) {
+ UUID client_id;
+ if (!settings->GetClientID(&client_id)) {
+ return EXIT_FAILURE;
+ }
+
+ const char* prefix = (show_operations > 1) ? "Client ID: " : "";
+
+ printf("%s%s\n", prefix, client_id.ToString().c_str());
+ }
+
+ if (options.show_uploads_enabled) {
+ bool uploads_enabled;
+ if (!settings->GetUploadsEnabled(&uploads_enabled)) {
+ return EXIT_FAILURE;
+ }
+
+ const char* prefix = (show_operations > 1) ? "Uploads enabled: " : "";
+
+ printf("%s%s\n", prefix, BoolToString(uploads_enabled).c_str());
+ }
+
+ if (options.show_last_upload_attempt_time) {
+ time_t last_upload_attempt_time;
+ if (!settings->GetLastUploadAttemptTime(&last_upload_attempt_time)) {
+ return EXIT_FAILURE;
+ }
+
+ const char* prefix =
+ (show_operations > 1) ? "Last upload attempt time: " : "";
+
+ printf("%s%s (%ld)\n",
+ prefix,
+ TimeToString(last_upload_attempt_time, options.utc).c_str(),
+ static_cast<long>(last_upload_attempt_time));
+ }
+
+ if (options.show_pending_reports) {
+ std::vector<CrashReportDatabase::Report> pending_reports;
+ if (database->GetPendingReports(&pending_reports) !=
+ CrashReportDatabase::kNoError) {
+ return EXIT_FAILURE;
+ }
+
+ if (show_operations > 1) {
+ printf("Pending reports:\n");
+ }
+
+ ShowReports(pending_reports, show_operations > 1 ? 2 : 0, options);
+ }
+
+ if (options.show_completed_reports) {
+ std::vector<CrashReportDatabase::Report> completed_reports;
+ if (database->GetCompletedReports(&completed_reports) !=
+ CrashReportDatabase::kNoError) {
+ return EXIT_FAILURE;
+ }
+
+ if (show_operations > 1) {
+ printf("Completed reports:\n");
+ }
+
+ ShowReports(completed_reports, show_operations > 1 ? 2 : 0, options);
+ }
+
+ for (const UUID& uuid : options.show_reports) {
+ CrashReportDatabase::Report report;
+ CrashReportDatabase::OperationStatus status =
+ database->LookUpCrashReport(uuid, &report);
+ if (status == CrashReportDatabase::kNoError) {
+ if (show_operations > 1) {
+ printf("Report %s:\n", uuid.ToString().c_str());
+ }
+ ShowReport(report, show_operations > 1 ? 2 : 0, options.utc);
+ } else if (status == CrashReportDatabase::kReportNotFound) {
+ // If only asked to do one thing, a failure to find the single requested
+ // report should result in a failure exit status.
+ if (show_operations + set_operations == 1) {
+ fprintf(
+ stderr, "%" PRFilePath ": Report not found\n", me.value().c_str());
+ return EXIT_FAILURE;
+ }
+ printf("Report %s not found\n", uuid.ToString().c_str());
+ } else {
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (options.has_set_uploads_enabled &&
+ !settings->SetUploadsEnabled(options.set_uploads_enabled)) {
+ return EXIT_FAILURE;
+ }
+
+ if (options.set_last_upload_attempt_time_string &&
+ !settings->SetLastUploadAttemptTime(
+ options.set_last_upload_attempt_time)) {
+ return EXIT_FAILURE;
+ }
+
+ for (const base::FilePath new_report_path : options.new_report_paths) {
+ FileReader file_reader;
+ if (!file_reader.Open(new_report_path)) {
+ return EXIT_FAILURE;
+ }
+
+ CrashReportDatabase::NewReport* new_report;
+ CrashReportDatabase::OperationStatus status =
+ database->PrepareNewCrashReport(&new_report);
+ if (status != CrashReportDatabase::kNoError) {
+ return EXIT_FAILURE;
+ }
+
+ CrashReportDatabase::CallErrorWritingCrashReport
+ call_error_writing_crash_report(database.get(), new_report);
+
+ char buf[4096];
+ ssize_t read_result;
+ while ((read_result = file_reader.Read(buf, sizeof(buf))) > 0) {
+ if (!LoggingWriteFile(new_report->handle, buf, read_result)) {
+ return EXIT_FAILURE;
+ }
+ }
+ if (read_result < 0) {
+ return EXIT_FAILURE;
+ }
+
+ call_error_writing_crash_report.Disarm();
+
+ UUID uuid;
+ status = database->FinishedWritingCrashReport(new_report, &uuid);
+ if (status != CrashReportDatabase::kNoError) {
+ return EXIT_FAILURE;
+ }
+
+ const char* prefix = (show_operations > 1) ? "New report ID: " : "";
+ printf("%s%s\n", prefix, uuid.ToString().c_str());
+ }
+
+ return EXIT_SUCCESS;
+}
+
+} // namespace
+} // namespace crashpad
+
+#if defined(OS_POSIX)
+int main(int argc, char* argv[]) {
+ return crashpad::DatabaseUtilMain(argc, argv);
+}
+#elif defined(OS_WIN)
+int wmain(int argc, wchar_t* argv[]) {
+ return crashpad::ToolSupport::Wmain(argc, argv, crashpad::DatabaseUtilMain);
+}
+#endif // OS_POSIX
diff --git a/chromium/third_party/crashpad/crashpad/tools/generate_dump.ad b/chromium/third_party/crashpad/crashpad/tools/generate_dump.ad
new file mode 100644
index 00000000000..d8a53c8d68b
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/tools/generate_dump.ad
@@ -0,0 +1,96 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+:doctype: manpage
+
+= generate_dump(1)
+
+== Name
+
+generate_dump - Generate a minidump file containing a snapshot of a running
+process
+
+== Synopsis
+
+[verse]
+*generate_dump* ['OPTION…'] 'PID'
+
+== Description
+
+Generates a minidump file containing a snapshot of a running process whose
+process identifier is 'PID'. By default, the target process will be suspended
+while the minidump is generated, and the minidump file will be written to
++minidump.PID+. After the minidump file is generated, the target process resumes
+running.
+
+The minidump file will contain information about the process, its threads, its
+modules, and the system. It will not contain any exception information because
+it will be generated from a live running process, not as a result of an
+exception occurring.
+
+This program uses +task_for_pid()+ to access the process’ task port. This
+operation may be restricted to use by the superuser, executables signed by an
+authority trusted by the system, and processes otherwise permitted by
+taskgated(8). Consequently, this program must normally either be signed or be
+invoked by root. It is possible to install this program as a setuid root
+executable to overcome this limitation.
+
+This program is similar to the gcore(1) program available on some operating
+systems.
+
+== Options
+
+*-r*, *--no-suspend*::
+The target process will continue running while the minidump file is generated.
+Normally, the target process is suspended during this operation, which
+guarantees that the minidump file will contain an atomic snapshot of the
+process.
++
+This option may be useful when attempting to generate a minidump from a process
+that dump generation has an interprocess dependency on, such as a system server
+like launchd(8) or opendirectoryd(8). Deadlock could occur if any portion of the
+dump generation operation blocks while waiting for a response from one of these
+servers while they are suspended.
+
+*-o*, *--output*='FILE'::
+The minidump will be written to 'FILE' instead of +minidump.PID+.
+
+*--help*::
+Display help and exit.
+
+*--version*::
+Output version information and exit.
+
+== Examples
+
+Generate a minidump file in +/tmp/minidump+ containing a snapshot of the process
+with PID 1234.
+[subs="quotes"]
+----
+$ *generate_dump --output=/tmp/minidump 1234*
+----
+
+== Exit Status
+
+*0*::
+Success.
+
+*1*::
+Failure, with a message printed to the standard error stream.
+
+== See Also
+
+man_link:catch_exception_tool[1]
+
+include::../doc/support/man_footer.ad[]
diff --git a/chromium/third_party/crashpad/crashpad/tools/generate_dump.cc b/chromium/third_party/crashpad/crashpad/tools/generate_dump.cc
new file mode 100644
index 00000000000..818d232dfb5
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/tools/generate_dump.cc
@@ -0,0 +1,223 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/stringprintf.h"
+#include "build/build_config.h"
+#include "minidump/minidump_file_writer.h"
+#include "tools/tool_support.h"
+#include "util/file/file_writer.h"
+#include "util/posix/drop_privileges.h"
+#include "util/stdlib/string_number_conversion.h"
+
+#if defined(OS_MACOSX)
+#include <mach/mach.h>
+#include <unistd.h>
+
+#include "base/mac/scoped_mach_port.h"
+#include "snapshot/mac/process_snapshot_mac.h"
+#include "util/mach/scoped_task_suspend.h"
+#include "util/mach/task_for_pid.h"
+#elif defined(OS_WIN)
+#include "base/strings/utf_string_conversions.h"
+#include "snapshot/win/process_snapshot_win.h"
+#endif // OS_MACOSX
+
+namespace crashpad {
+namespace {
+
+struct Options {
+ std::string dump_path;
+ pid_t pid;
+ bool suspend;
+};
+
+void Usage(const base::FilePath& me) {
+ fprintf(stderr,
+"Usage: %" PRFilePath " [OPTION]... PID\n"
+"Generate a minidump file containing a snapshot of a running process.\n"
+"\n"
+" -r, --no-suspend don't suspend the target process during dump generation\n"
+" -o, --output=FILE write the minidump to FILE instead of minidump.PID\n"
+" --help display this help and exit\n"
+" --version output version information and exit\n",
+ me.value().c_str());
+ ToolSupport::UsageTail(me);
+}
+
+int GenerateDumpMain(int argc, char* argv[]) {
+ const base::FilePath argv0(
+ ToolSupport::CommandLineArgumentToFilePathStringType(argv[0]));
+ const base::FilePath me(argv0.BaseName());
+
+ enum OptionFlags {
+ // “Short” (single-character) options.
+ kOptionOutput = 'o',
+ kOptionNoSuspend = 'r',
+
+ // Long options without short equivalents.
+ kOptionLastChar = 255,
+
+ // Standard options.
+ kOptionHelp = -2,
+ kOptionVersion = -3,
+ };
+
+ Options options = {};
+ options.suspend = true;
+
+ const option long_options[] = {
+ {"no-suspend", no_argument, nullptr, kOptionNoSuspend},
+ {"output", required_argument, nullptr, kOptionOutput},
+ {"help", no_argument, nullptr, kOptionHelp},
+ {"version", no_argument, nullptr, kOptionVersion},
+ {nullptr, 0, nullptr, 0},
+ };
+
+ int opt;
+ while ((opt = getopt_long(argc, argv, "o:r", long_options, nullptr)) != -1) {
+ switch (opt) {
+ case kOptionOutput:
+ options.dump_path = optarg;
+ break;
+ case kOptionNoSuspend:
+ options.suspend = false;
+ break;
+ case kOptionHelp:
+ Usage(me);
+ return EXIT_SUCCESS;
+ case kOptionVersion:
+ ToolSupport::Version(me);
+ return EXIT_SUCCESS;
+ default:
+ ToolSupport::UsageHint(me, nullptr);
+ return EXIT_FAILURE;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1) {
+ ToolSupport::UsageHint(me, "PID is required");
+ return EXIT_FAILURE;
+ }
+
+ if (!StringToNumber(argv[0], &options.pid) || options.pid <= 0) {
+ fprintf(stderr,
+ "%" PRFilePath ": invalid PID: %s\n",
+ me.value().c_str(),
+ argv[0]);
+ return EXIT_FAILURE;
+ }
+
+#if defined(OS_MACOSX)
+ task_t task = TaskForPID(options.pid);
+ if (task == TASK_NULL) {
+ return EXIT_FAILURE;
+ }
+ base::mac::ScopedMachSendRight task_owner(task);
+
+ // This tool may have been installed as a setuid binary so that TaskForPID()
+ // could succeed. Drop any privileges now that they’re no longer necessary.
+ DropPrivileges();
+
+ if (options.pid == getpid()) {
+ if (options.suspend) {
+ LOG(ERROR) << "cannot suspend myself";
+ return EXIT_FAILURE;
+ }
+ LOG(WARNING) << "operating on myself";
+ }
+#elif defined(OS_WIN)
+ ScopedKernelHANDLE process(
+ OpenProcess(PROCESS_ALL_ACCESS, false, options.pid));
+ if (!process.is_valid()) {
+ LOG(ERROR) << "could not open process " << options.pid;
+ return EXIT_FAILURE;
+ }
+#endif // OS_MACOSX
+
+ if (options.dump_path.empty()) {
+ options.dump_path = base::StringPrintf("minidump.%d", options.pid);
+ }
+
+ {
+#if defined(OS_MACOSX)
+ scoped_ptr<ScopedTaskSuspend> suspend;
+ if (options.suspend) {
+ suspend.reset(new ScopedTaskSuspend(task));
+ }
+#elif defined(OS_WIN)
+ if (options.suspend) {
+ LOG(ERROR) << "TODO(scottmg): --no-suspend is required for now.";
+ return EXIT_FAILURE;
+ }
+#endif // OS_MACOSX
+
+#if defined(OS_MACOSX)
+ ProcessSnapshotMac process_snapshot;
+ if (!process_snapshot.Initialize(task)) {
+ return EXIT_FAILURE;
+ }
+#elif defined(OS_WIN)
+ ProcessSnapshotWin process_snapshot;
+ if (!process_snapshot.Initialize(process.get())) {
+ return EXIT_FAILURE;
+ }
+#endif // OS_MACOSX
+
+ FileWriter file_writer;
+ base::FilePath dump_path(
+ ToolSupport::CommandLineArgumentToFilePathStringType(
+ options.dump_path));
+ if (!file_writer.Open(dump_path,
+ FileWriteMode::kTruncateOrCreate,
+ FilePermissions::kWorldReadable)) {
+ return EXIT_FAILURE;
+ }
+
+ MinidumpFileWriter minidump;
+ minidump.InitializeFromSnapshot(&process_snapshot);
+ if (!minidump.WriteEverything(&file_writer)) {
+ if (unlink(options.dump_path.c_str()) != 0) {
+ PLOG(ERROR) << "unlink";
+ }
+ return EXIT_FAILURE;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
+
+} // namespace
+} // namespace crashpad
+
+#if defined(OS_POSIX)
+int main(int argc, char* argv[]) {
+ return crashpad::GenerateDumpMain(argc, argv);
+}
+#elif defined(OS_WIN)
+int wmain(int argc, wchar_t* argv[]) {
+ return crashpad::ToolSupport::Wmain(argc, argv, crashpad::GenerateDumpMain);
+}
+#endif // OS_POSIX
diff --git a/chromium/third_party/crashpad/crashpad/tools/mac/catch_exception_tool.ad b/chromium/third_party/crashpad/crashpad/tools/mac/catch_exception_tool.ad
new file mode 100644
index 00000000000..a639de4cc63
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/tools/mac/catch_exception_tool.ad
@@ -0,0 +1,117 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+:doctype: manpage
+
+= catch_exception_tool(1)
+
+== Name
+
+catch_exception_tool - Catch Mach exceptions and display information about them
+
+== Synopsis
+
+[verse]
+*catch_exception_tool* *-m* 'SERVICE' ['OPTION…']
+
+== Description
+
+Runs a Mach exception server registered with the bootstrap server under the name
+'SERVICE'. The exception server is capable of receiving exceptions for
+“behavior” values of +EXCEPTION_DEFAULT+, +EXCEPTION_STATE+, and
++EXCEPTION_STATE_IDENTITY+, with or without +MACH_EXCEPTION_CODES+ set.
+
+== Options
+
+*-f*, *--file*='FILE'::
+Information about the exception will be appended to 'FILE' instead of the
+standard output stream.
+
+*-m*, *--mach-service*='SERVICE'::
+Check in with the bootstrap server under the name 'SERVICE'. This service name
+may already be reserved with the bootstrap server in cases where this tool is
+started by launchd(8) as a result of a message being sent to a service declared
+in a job’s +MachServices+ dictionary (see launchd.plist(5)). The service name
+may also be completely unknown to the system.
+
+*-p*, *--persistent*::
+Continue processing exceptions after the first one. The default mode is
+one-shot, where this tool exits after processing the first exception.
+
+*-t*, *--timeout*='TIMEOUT'::
+Run for a maximum of 'TIMEOUT' seconds. Specify +0+ to request non-blocking
+operation, in which the tool exits immediately if no exception is received. In
+*--persistent* mode, 'TIMEOUT' applies to the overall duration that this tool
+will run, not to the processing of individual exceptions. When *--timeout* is
+not specified, this tool will block indefinitely while waiting for an exception.
+
+*--help*::
+Display help and exit.
+
+*--version*::
+Output version information and exit.
+
+== Examples
+
+Run a one-shot blocking exception server registered with the bootstrap server
+under the name +svc+:
+[subs="quotes"]
+----
+$ *catch_exception_tool --mach-service=svc --file=out &amp;*
+[1] 1233
+$ *exception_port_tool --set-handler=handler=bootstrap:svc crasher*
+Illegal instruction: 4
+[1]+ Done catch_exception_tool --mach-service=svc --file=out
+$ *cat out*
+catch_exception_tool:
+behavior EXCEPTION_DEFAULT|MACH_EXCEPTION_CODES,
+pid 1234, thread 56789, exception EXC_CRASH, codes[2] 0x4200001, 0,
+original exception EXC_BAD_INSTRUCTION, original code[0] 1,
+signal SIGILL
+----
+
+Run an on-demand exception server started by launchd(5) available via the
+bootstrap server under the name +svc+:
+[subs="quotes"]
+----
+$ *on_demand_service_tool --load --label=catch_exception \
+ --mach-service=svc \
+ $(which catch_exception_tool) --mach-service=svc \
+ --file=/tmp/out --persistent --timeout=0*
+$ *exception_port_tool --set-handler=handler=bootstrap:svc crasher*
+Illegal instruction: 4
+$ *on_demand_service_tool --unload --label=catch_exception*
+$ *cat /tmp/out*
+catch_exception_tool:
+behavior EXCEPTION_DEFAULT|MACH_EXCEPTION_CODES,
+pid 2468, thread 13579, exception EXC_CRASH, codes[2] 0x4200001, 0,
+original exception EXC_BAD_INSTRUCTION, original code[0] 1,
+signal SIGILL
+----
+
+== Exit Status
+
+*0*::
+Success. In *--persistent* mode with a *--timeout* set, it is considered
+successful if at least one exception was caught when the timer expires.
+
+*1*::
+Failure, with a message printed to the standard error stream.
+
+== See Also
+
+man_link:exception_port_tool[1],
+man_link:on_demand_service_tool[1]
+
+include::../../doc/support/man_footer.ad[]
diff --git a/chromium/third_party/crashpad/crashpad/tools/mac/catch_exception_tool.cc b/chromium/third_party/crashpad/crashpad/tools/mac/catch_exception_tool.cc
new file mode 100644
index 00000000000..d3b2dd58854
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/tools/mac/catch_exception_tool.cc
@@ -0,0 +1,314 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <getopt.h>
+#include <libgen.h>
+#include <servers/bootstrap.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "tools/tool_support.h"
+#include "util/mach/exc_server_variants.h"
+#include "util/mach/exception_behaviors.h"
+#include "util/mach/exception_types.h"
+#include "util/mach/mach_extensions.h"
+#include "util/mach/mach_message.h"
+#include "util/mach/mach_message_server.h"
+#include "util/mach/symbolic_constants_mach.h"
+#include "util/posix/symbolic_constants_posix.h"
+#include "util/stdlib/string_number_conversion.h"
+
+namespace crashpad {
+namespace {
+
+struct Options {
+ std::string file_path;
+ std::string mach_service;
+ FILE* file;
+ int timeout_secs;
+ bool has_timeout;
+ MachMessageServer::Persistent persistent;
+};
+
+class ExceptionServer : public UniversalMachExcServer::Interface {
+ public:
+ ExceptionServer(const Options& options,
+ const std::string& me,
+ int* exceptions_handled)
+ : UniversalMachExcServer::Interface(),
+ options_(options),
+ me_(me),
+ exceptions_handled_(exceptions_handled) {}
+
+ // UniversalMachExcServer::Interface:
+ virtual kern_return_t CatchMachException(
+ exception_behavior_t behavior,
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_complex_request) override {
+ *destroy_complex_request = true;
+ ++*exceptions_handled_;
+
+ fprintf(options_.file,
+ "%s: behavior %s, ",
+ me_.c_str(),
+ ExceptionBehaviorToString(
+ behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str());
+
+ kern_return_t kr;
+ if (ExceptionBehaviorHasIdentity(behavior)) {
+ pid_t pid;
+ kr = pid_for_task(task, &pid);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "pid_for_task";
+ return KERN_FAILURE;
+ }
+ fprintf(options_.file, "pid %d, ", pid);
+
+ thread_identifier_info identifier_info;
+ mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
+ kr = thread_info(thread,
+ THREAD_IDENTIFIER_INFO,
+ reinterpret_cast<thread_info_t>(&identifier_info),
+ &count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "thread_info";
+ return KERN_FAILURE;
+ }
+ fprintf(options_.file, "thread %lld, ", identifier_info.thread_id);
+ }
+
+ fprintf(
+ options_.file,
+ "exception %s, codes[%d] ",
+ ExceptionToString(exception, kUseFullName | kUnknownIsNumeric).c_str(),
+ code_count);
+
+ for (size_t index = 0; index < code_count; ++index) {
+ fprintf(options_.file,
+ "%#llx%s",
+ code[index],
+ index != code_count - 1 ? ", " : "");
+ }
+
+ if (exception == EXC_CRASH) {
+ mach_exception_code_t original_code_0;
+ int signal;
+ exception_type_t original_exception =
+ ExcCrashRecoverOriginalException(code[0], &original_code_0, &signal);
+ fprintf(options_.file,
+ ", original exception %s, original code[0] %lld, signal %s",
+ ExceptionToString(original_exception,
+ kUseFullName | kUnknownIsNumeric).c_str(),
+ original_code_0,
+ SignalToString(signal, kUseFullName | kUnknownIsNumeric).c_str());
+ }
+
+ if (ExceptionBehaviorHasState(behavior)) {
+ std::string flavor_string =
+ ThreadStateFlavorToString(*flavor, kUseFullName | kUnknownIsNumeric);
+ fprintf(options_.file,
+ ", flavor %s, old_state_count %d",
+ flavor_string.c_str(),
+ old_state_count);
+ }
+
+ fprintf(options_.file, "\n");
+ fflush(options_.file);
+
+ if (exception != EXC_CRASH && exception != kMachExceptionSimulated) {
+ // Find another handler.
+ return KERN_FAILURE;
+ }
+
+ ExcServerCopyState(
+ behavior, old_state, old_state_count, new_state, new_state_count);
+
+ return ExcServerSuccessfulReturnValue(behavior, false);
+ }
+
+ private:
+ const Options& options_;
+ const std::string& me_;
+ int* exceptions_handled_;
+};
+
+void Usage(const std::string& me) {
+ fprintf(stderr,
+"Usage: %s -m SERVICE [OPTION]...\n"
+"Catch Mach exceptions and display information about them.\n"
+"\n"
+" -f, --file=FILE append information to FILE instead of stdout\n"
+" -m, --mach-service=SERVICE register SERVICE with the bootstrap server\n"
+" -p, --persistent continue processing exceptions after the first\n"
+" -t, --timeout=TIMEOUT run for a maximum of TIMEOUT seconds\n"
+" --help display this help and exit\n"
+" --version output version information and exit\n",
+ me.c_str());
+ ToolSupport::UsageTail(me);
+}
+
+int CatchExceptionToolMain(int argc, char* argv[]) {
+ const std::string me(basename(argv[0]));
+
+ enum OptionFlags {
+ // “Short” (single-character) options.
+ kOptionFile = 'f',
+ kOptionMachService = 'm',
+ kOptionPersistent = 'p',
+ kOptionTimeout = 't',
+
+ // Long options without short equivalents.
+ kOptionLastChar = 255,
+
+ // Standard options.
+ kOptionHelp = -2,
+ kOptionVersion = -3,
+ };
+
+ Options options = {};
+
+ const option long_options[] = {
+ {"file", required_argument, nullptr, kOptionFile},
+ {"mach-service", required_argument, nullptr, kOptionMachService},
+ {"persistent", no_argument, nullptr, kOptionPersistent},
+ {"timeout", required_argument, nullptr, kOptionTimeout},
+ {"help", no_argument, nullptr, kOptionHelp},
+ {"version", no_argument, nullptr, kOptionVersion},
+ {nullptr, 0, nullptr, 0},
+ };
+
+ int opt;
+ while ((opt = getopt_long(argc, argv, "f:m:pt:", long_options, nullptr)) !=
+ -1) {
+ switch (opt) {
+ case kOptionFile:
+ options.file_path = optarg;
+ break;
+ case kOptionMachService:
+ options.mach_service = optarg;
+ break;
+ case kOptionPersistent:
+ options.persistent = MachMessageServer::kPersistent;
+ break;
+ case kOptionTimeout:
+ if (!StringToNumber(optarg, &options.timeout_secs) ||
+ options.timeout_secs < 0) {
+ ToolSupport::UsageHint(me, "-t requires a zero or positive TIMEOUT");
+ return EXIT_FAILURE;
+ }
+ options.has_timeout = true;
+ break;
+ case kOptionHelp:
+ Usage(me);
+ return EXIT_SUCCESS;
+ case kOptionVersion:
+ ToolSupport::Version(me);
+ return EXIT_SUCCESS;
+ default:
+ ToolSupport::UsageHint(me, nullptr);
+ return EXIT_FAILURE;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (options.mach_service.empty()) {
+ ToolSupport::UsageHint(me, "-m is required");
+ return EXIT_FAILURE;
+ }
+
+ mach_port_t service_port;
+ kern_return_t kr = bootstrap_check_in(
+ bootstrap_port, options.mach_service.c_str(), &service_port);
+ if (kr != BOOTSTRAP_SUCCESS) {
+ BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_check_in " << options.mach_service;
+ return EXIT_FAILURE;
+ }
+
+ base::ScopedFILE file_owner;
+ if (options.file_path.empty()) {
+ options.file = stdout;
+ } else {
+ file_owner.reset(fopen(options.file_path.c_str(), "a"));
+ if (!file_owner.get()) {
+ PLOG(ERROR) << "fopen " << options.file_path;
+ return EXIT_FAILURE;
+ }
+ options.file = file_owner.get();
+ }
+
+ int exceptions_handled = 0;
+ ExceptionServer exception_server(options, me, &exceptions_handled);
+ UniversalMachExcServer universal_mach_exc_server(&exception_server);
+
+ // Assume that if persistent mode has been requested, it’s desirable to ignore
+ // large messages and keep running.
+ MachMessageServer::ReceiveLarge receive_large =
+ (options.persistent == MachMessageServer::kPersistent)
+ ? MachMessageServer::kReceiveLargeIgnore
+ : MachMessageServer::kReceiveLargeError;
+
+ mach_msg_timeout_t timeout_ms;
+ if (!options.has_timeout) {
+ timeout_ms = kMachMessageTimeoutWaitIndefinitely;
+ } else if (options.timeout_secs == 0) {
+ timeout_ms = kMachMessageTimeoutNonblocking;
+ } else {
+ timeout_ms = options.timeout_secs * 1000;
+ }
+
+ mach_msg_return_t mr = MachMessageServer::Run(&universal_mach_exc_server,
+ service_port,
+ MACH_MSG_OPTION_NONE,
+ options.persistent,
+ receive_large,
+ timeout_ms);
+ if (mr == MACH_RCV_TIMED_OUT && options.has_timeout && options.persistent &&
+ exceptions_handled) {
+ // This is not an error: when exiting on timeout during persistent
+ // processing and at least one exception was handled, it’s considered a
+ // success.
+ } else if (mr != MACH_MSG_SUCCESS) {
+ MACH_LOG(ERROR, mr) << "MachMessageServer::Run";
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+} // namespace
+} // namespace crashpad
+
+int main(int argc, char* argv[]) {
+ return crashpad::CatchExceptionToolMain(argc, argv);
+}
diff --git a/chromium/third_party/crashpad/crashpad/tools/mac/exception_port_tool.ad b/chromium/third_party/crashpad/crashpad/tools/mac/exception_port_tool.ad
new file mode 100644
index 00000000000..3ef36f123d2
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/tools/mac/exception_port_tool.ad
@@ -0,0 +1,188 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+:doctype: manpage
+
+= exception_port_tool(1)
+
+== Name
+
+exception_port_tool - Show and change Mach exception ports
+
+== Synopsis
+
+[verse]
+*exception_port_tool* ['OPTION…'] ['COMMAND' ['ARG…']]
+
+== Description
+
+Shows Mach exception ports registered for a thread, task, or host target with a
+*--show-&#42;* option, changes Mach exception ports with *--set-handler*, shows
+changes with a *--show-new-&#42;* option, and executes 'COMMAND' along with any
+arguments specified ('ARG…') with the changed exception ports in effect.
+
+== Options
+*-s*, *--set-handler*='DESCRIPTION'::
+Set an exception port to 'DESCRIPTION'. This option may appear zero, one, or
+more times.
++
+'DESCRIPTION' is formatted as a comma-separated sequence of tokens, where each
+token consists of a key and value separated by an equals sign. These keys are
+recognized:
++
+*target*='TARGET':::
+'TARGET' defines which target’s exception ports to set: *host*, *task*, or
+*thread*. The default value of 'TARGET' is *task*. Operations on *host* are
+restricted to the superuser.
++
+*mask*='MASK':::
+'MASK' defines the mask of exception types to handle, from
++<mach/exception_types.h>+. This can be *BAD_ACCESS*, *BAD_INSTRUCTION*,
+*ARITHMETIC*, *EMULATION*, *SOFTWARE*, *BREAKPOINT*, *SYSCALL*, *MACH_SYSCALL*,
+*RPC_ALERT*, *CRASH*, *RESOURCE*, or *GUARD*. Different exception types may be
+combined by combining them with pipe characters (*|*). The special value *ALL*
+includes each exception type except for *CRASH*. To truly specify all exception
+types including *CRASH*, use *ALL|CRASH*. The default value of 'MASK' is
+*CRASH*.
++
+*behavior*='BEHAVIOR':::
+'BEHAVIOR' defines the specific exception handler routine to be called when an
+exception occurs. This can be *DEFAULT*, *STATE*, or *STATE_IDENTITY*. *MACH*
+may also be specified by combining them with pipe characters (*|*). The most
+complete set of exception information is provided with *STATE_IDENTITY|MACH*.
+Not all exception servers implement all possible behaviors. The default value of
+'BEHAVIOR' is *DEFAULT|MACH*.
++
+*flavor*='FLAVOR':::
+For state-carrying values of 'BEHAVIOR' (those including *STATE* or
+*STATE_IDENTITY*), 'FLAVOR' specifies the architecture-specific thread state
+flavor to be provided to the exception handler. For the x86 family, this can be
+*THREAD*, *THREAD32*, *THREAD64*, *FLOAT*, *FLOAT32*, *FLOAT64*, *DEBUG*,
+*DEBUG32*, or *DEBUG64*. The default value of 'FLAVOR' is *NONE*, which is not
+valid for state-carrying values of 'BEHAVIOR'.
++
+*handler*='HANDLER':::
+'HANDLER' defines the exception handler. *NULL* indicates that any existing
+exception port should be cleared. 'HANDLER' may also take the form
+*bootstrap*:__SERVICE__, which will look 'SERVICE' up with the bootstrap server
+and set that service as the exception handler. The default value of 'HANDLER' is
+*NULL*.
+
+*--show-bootstrap*='SERVICE'::
+Looks up 'SERVICE' with the bootstrap server and shows it. Normally, the handler
+port values displayed by the other *--show-&#42;* options are meaningless
+handles, but by comparing them to the port values for known bootstrap services,
+it is possible to verify that they are set as intended.
+
+*-p*, *--pid*='PID'::
+For operations on the task target, including *--set-handler* with 'TARGET' set
+to *task*, *--show-task*, and *--show-new-task*, operates on the task associated
+with process id 'PID' instead of the current task associated with the tool. When
+this option is supplied, 'COMMAND' must not be specified.
++
+This option uses +task_for_pid()+ to access the process’ task port. This
+operation may be restricted to use by the superuser, executables signed by an
+authority trusted by the system, and processes otherwise permitted by
+taskgated(8). Consequently, this program must normally either be signed or be
+invoked by root to use this option. It is possible to install this program as a
+setuid root executable to overcome this limitation.
+
+*-h*, *--show-host*::
+Shows the original host exception ports before making any changes requested by
+*--set-handler*. This option is restricted to the superuser.
+
+*-t*, *--show-task*::
+Shows the original task exception ports before making any changes requested by
+*--set-handler*.
+
+*--show-thread*::
+Shows the original thread exception ports before making any changes requested by
+*--set-handler*.
+
+*-H*, *--show-new-host*::
+Shows the modified host exception ports after making any changes requested by
+*--set-handler*. This option is restricted to the superuser.
+
+*-T*, *--show-new-task*::
+Shows the modified task exception ports after making any changes requested by
+*--set-handler*.
+
+*--show-new-thread*::
+Shows the modified thread exception ports after making any changes requested by
+*--set-handler*.
+
+*-n*, *--numeric*::
+For *--show-&#42;* options, all values will be displayed numerically only. The
+default is to decode numeric values and display them symbolically as well.
+
+*--help*::
+Display help and exit.
+
+*--version*::
+Output version information and exit.
+
+== Examples
+
+Sets a new task-level exception handler for +EXC_CRASH+-type exceptions to the
+handler registered with the bootstrap server as +svc+, showing the task-level
+exception ports before and after the change. The old and new exception handlers
+are verified by their service names as registered with the bootstrap server.
+With the new task-level exception ports in effect, a program is run.
+[subs="quotes"]
+----
+$ *exception_port_tool --show-task --show-new-task \
+ --show-bootstrap=com.apple.ReportCrash --show-bootstrap=svc \
+ --set-handler=behavior=DEFAULT,handler=bootstrap:svc crash*
+service com.apple.ReportCrash 0xe03
+service svc 0x1003
+task exception port 0, mask 0x400 (CRASH), port 0xe03,
+behavior 0x80000003 (STATE_IDENTITY|MACH), flavor 7 (THREAD)
+new task exception port 0, mask 0x400 (CRASH), port 0x1003,
+behavior 0x1 (DEFAULT), flavor 13 (NONE)
+Illegal instruction: 4
+----
+
+Shows the task-level exception ports for the process with PID 1234. This
+requires superuser permissions or the approval of taskgated(8).
+[subs="quotes"]
+----
+# *exception_port_tool --pid=1234 --show-task*
+task exception port 0, mask 0x4e
+(BAD_ACCESS|BAD_INSTRUCTION|ARITHMETIC|BREAKPOINT), port 0x1503,
+behavior 0x1 (DEFAULT), flavor 13 (NONE)
+task exception port 1, mask 0x1c00 (CRASH|RESOURCE|GUARD),
+port 0x1403, behavior 0x80000003 (STATE_IDENTITY|MACH),
+flavor 7 (THREAD)
+----
+
+== Exit Status
+
+*0*::
+Success.
+
+*125*::
+Failure, with a message printed to the standard error stream.
+
+*126*::
+The program specified by 'COMMAND' was found, but could not be invoked.
+
+*127*::
+The program specified by 'COMMAND' could not be found.
+
+== See Also
+
+man_link:catch_exception_tool[1],
+man_link:on_demand_service_tool[1]
+
+include::../../doc/support/man_footer.ad[]
diff --git a/chromium/third_party/crashpad/crashpad/tools/mac/exception_port_tool.cc b/chromium/third_party/crashpad/crashpad/tools/mac/exception_port_tool.cc
new file mode 100644
index 00000000000..3bb96524d97
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/tools/mac/exception_port_tool.cc
@@ -0,0 +1,593 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <errno.h>
+#include <getopt.h>
+#include <libgen.h>
+#include <mach/mach.h>
+#include <servers/bootstrap.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/mac/mach_logging.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/strings/stringprintf.h"
+#include "tools/tool_support.h"
+#include "util/mach/exception_ports.h"
+#include "util/mach/mach_extensions.h"
+#include "util/mach/symbolic_constants_mach.h"
+#include "util/mach/task_for_pid.h"
+#include "util/posix/drop_privileges.h"
+#include "util/stdlib/string_number_conversion.h"
+
+namespace crashpad {
+namespace {
+
+//! \brief Manages a pool of Mach send rights, deallocating all send rights upon
+//! destruction.
+//!
+//! This class effectively implements what a vector of
+//! base::mac::ScopedMachSendRight objects would be.
+//!
+//! The various “show” operations performed by this program display Mach ports
+//! by their names as they are known in this task. For this to be useful, rights
+//! to the same ports must have consistent names across successive calls. This
+//! cannot be guaranteed if the rights are deallocated as soon as they are used,
+//! because if that deallocation causes the task to lose its last right to a
+//! port, subsequently regaining a right to the same port would cause it to be
+//! known by a new name in this task.
+//!
+//! Instead of immediately deallocating send rights that are used for display,
+//! they can be added to this pool. The pool collects send rights, ensuring that
+//! they remain alive in this task, and that subsequent calls that obtain the
+//! same rights cause them to be known by the same name. All rights are
+//! deallocated upon destruction.
+class MachSendRightPool {
+ public:
+ MachSendRightPool()
+ : send_rights_() {
+ }
+
+ ~MachSendRightPool() {
+ for (mach_port_t send_right : send_rights_) {
+ kern_return_t kr = mach_port_deallocate(mach_task_self(), send_right);
+ MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_port_deallocate";
+ }
+ }
+
+ //! \brief Adds a send right to the pool.
+ //!
+ //! \param[in] send_right The send right to be added. The pool object takes
+ //! ownership of the send right, which remains valid until the pool object
+ //! is destroyed.
+ //!
+ //! It is possible and in fact likely that one pool will wind up owning the
+ //! same send right multiple times. This is acceptable, because send rights
+ //! are reference-counted.
+ void AddSendRight(mach_port_t send_right) {
+ send_rights_.push_back(send_right);
+ }
+
+ private:
+ std::vector<mach_port_t> send_rights_;
+
+ DISALLOW_COPY_AND_ASSIGN(MachSendRightPool);
+};
+
+struct ExceptionHandlerDescription {
+ ExceptionPorts::TargetType target_type;
+ exception_mask_t mask;
+ exception_behavior_t behavior;
+ thread_state_flavor_t flavor;
+ std::string handler;
+};
+
+const char kHandlerNull[] = "NULL";
+const char kHandlerBootstrapColon[] = "bootstrap:";
+
+// Populates |description| based on a textual representation in
+// |handler_string_ro|, returning true on success and false on failure (parse
+// error). The --help string describes the format of |handler_string_ro|.
+// Briefly, it is a comma-separated string that allows the members of
+// |description| to be specified as "field=value". Values for "target" can be
+// "host", "task", or "thread"; values for "handler" are of the form
+// "bootstrap:service_name" where service_name will be looked up with the
+// bootstrap server; and values for the other fields are interpreted by
+// SymbolicConstantsMach.
+bool ParseHandlerString(const char* handler_string_ro,
+ ExceptionHandlerDescription* description) {
+ const char kTargetEquals[] = "target=";
+ const char kMaskEquals[] = "mask=";
+ const char kBehaviorEquals[] = "behavior=";
+ const char kFlavorEquals[] = "flavor=";
+ const char kHandlerEquals[] = "handler=";
+
+ std::string handler_string(handler_string_ro);
+ char* handler_string_c = &handler_string[0];
+
+ char* token;
+ while ((token = strsep(&handler_string_c, ",")) != nullptr) {
+ if (strncmp(token, kTargetEquals, strlen(kTargetEquals)) == 0) {
+ const char* value = token + strlen(kTargetEquals);
+ if (strcmp(value, "host") == 0) {
+ description->target_type = ExceptionPorts::kTargetTypeHost;
+ } else if (strcmp(value, "task") == 0) {
+ description->target_type = ExceptionPorts::kTargetTypeTask;
+ } else if (strcmp(value, "thread") == 0) {
+ description->target_type = ExceptionPorts::kTargetTypeThread;
+ } else {
+ return false;
+ }
+ } else if (strncmp(token, kMaskEquals, strlen(kMaskEquals)) == 0) {
+ const char* value = token + strlen(kMaskEquals);
+ if (!StringToExceptionMask(
+ value,
+ kAllowFullName | kAllowShortName | kAllowNumber | kAllowOr,
+ &description->mask)) {
+ return false;
+ }
+ } else if (strncmp(token, kBehaviorEquals, strlen(kBehaviorEquals)) == 0) {
+ const char* value = token + strlen(kBehaviorEquals);
+ if (!StringToExceptionBehavior(
+ value,
+ kAllowFullName | kAllowShortName | kAllowNumber,
+ &description->behavior)) {
+ return false;
+ }
+ } else if (strncmp(token, kFlavorEquals, strlen(kFlavorEquals)) == 0) {
+ const char* value = token + strlen(kFlavorEquals);
+ if (!StringToThreadStateFlavor(
+ value,
+ kAllowFullName | kAllowShortName | kAllowNumber,
+ &description->flavor)) {
+ return false;
+ }
+ } else if (strncmp(token, kHandlerEquals, strlen(kHandlerEquals)) == 0) {
+ const char* value = token + strlen(kHandlerEquals);
+ if (strcmp(value, kHandlerNull) != 0 &&
+ strncmp(value,
+ kHandlerBootstrapColon,
+ strlen(kHandlerBootstrapColon)) != 0) {
+ return false;
+ }
+ description->handler = std::string(value);
+ } else {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// ShowExceptionPorts() shows handlers as numeric mach_port_t values, which are
+// opaque and meaningless on their own. ShowBootstrapService() can be used to
+// look up a service with the bootstrap server by name and show its mach_port_t
+// value, which can then be associated with handlers shown by
+// ShowExceptionPorts(). Any send rights obtained by this function are added to
+// |mach_send_right_pool|.
+void ShowBootstrapService(const std::string& service_name,
+ MachSendRightPool* mach_send_right_pool) {
+ mach_port_t service_port;
+ kern_return_t kr = bootstrap_look_up(
+ bootstrap_port, service_name.c_str(), &service_port);
+ if (kr != BOOTSTRAP_SUCCESS) {
+ BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up " << service_name;
+ return;
+ }
+
+ mach_send_right_pool->AddSendRight(service_port);
+
+ printf("service %s %#x\n", service_name.c_str(), service_port);
+}
+
+// Prints information about all exception ports known for |exception_ports|. If
+// |numeric| is true, all information is printed in numeric form, otherwise, it
+// will be converted to symbolic constants where possible by
+// SymbolicConstantsMach. If |is_new| is true, information will be presented as
+// “new exception ports”, indicating that they show the state of the exception
+// ports after SetExceptionPort() has been called. Any send rights obtained by
+// this function are added to |mach_send_right_pool|.
+void ShowExceptionPorts(const ExceptionPorts& exception_ports,
+ bool numeric,
+ bool is_new,
+ MachSendRightPool* mach_send_right_pool) {
+ const char* target_name = exception_ports.TargetTypeName();
+
+ std::vector<ExceptionPorts::ExceptionHandler> handlers;
+ if (!exception_ports.GetExceptionPorts(ExcMaskAll() | EXC_MASK_CRASH,
+ &handlers)) {
+ return;
+ }
+
+ const char* age_name = is_new ? "new " : "";
+
+ if (handlers.size() == 0) {
+ printf("no %s%s exception ports\n", age_name, target_name);
+ }
+
+ for (size_t port_index = 0; port_index < handlers.size(); ++port_index) {
+ mach_send_right_pool->AddSendRight(handlers[port_index].port);
+
+ if (numeric) {
+ printf(
+ "%s%s exception port %zu, mask %#x, port %#x, "
+ "behavior %#x, flavor %u\n",
+ age_name,
+ target_name,
+ port_index,
+ handlers[port_index].mask,
+ handlers[port_index].port,
+ handlers[port_index].behavior,
+ handlers[port_index].flavor);
+ } else {
+ std::string mask_string = ExceptionMaskToString(
+ handlers[port_index].mask, kUseShortName | kUnknownIsEmpty | kUseOr);
+ if (mask_string.empty()) {
+ mask_string.assign("?");
+ }
+
+ std::string behavior_string = ExceptionBehaviorToString(
+ handlers[port_index].behavior, kUseShortName | kUnknownIsEmpty);
+ if (behavior_string.empty()) {
+ behavior_string.assign("?");
+ }
+
+ std::string flavor_string = ThreadStateFlavorToString(
+ handlers[port_index].flavor, kUseShortName | kUnknownIsEmpty);
+ if (flavor_string.empty()) {
+ flavor_string.assign("?");
+ }
+
+ printf(
+ "%s%s exception port %zu, mask %#x (%s), port %#x, "
+ "behavior %#x (%s), flavor %u (%s)\n",
+ age_name,
+ target_name,
+ port_index,
+ handlers[port_index].mask,
+ mask_string.c_str(),
+ handlers[port_index].port,
+ handlers[port_index].behavior,
+ behavior_string.c_str(),
+ handlers[port_index].flavor,
+ flavor_string.c_str());
+ }
+ }
+}
+
+// Sets the exception port for |target_port|, a send right to a thread, task, or
+// host port, to |description|, which identifies what type of port |target_port|
+// is and describes an exception port to be set. Returns true on success.
+//
+// This function may be called more than once if setting different handlers is
+// desired.
+bool SetExceptionPort(const ExceptionHandlerDescription* description,
+ mach_port_t target_port) {
+ base::mac::ScopedMachSendRight service_port_owner;
+ exception_handler_t service_port = MACH_PORT_NULL;
+ kern_return_t kr;
+ if (description->handler.compare(
+ 0, strlen(kHandlerBootstrapColon), kHandlerBootstrapColon) == 0) {
+ const char* service_name =
+ description->handler.c_str() + strlen(kHandlerBootstrapColon);
+ kr = bootstrap_look_up(bootstrap_port, service_name, &service_port);
+ if (kr != BOOTSTRAP_SUCCESS) {
+ BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up " << service_name;
+ return false;
+ }
+
+ // The service port doesn’t need to be added to a MachSendRightPool because
+ // it’s not used for display at all. ScopedMachSendRight is sufficient.
+ service_port_owner.reset(service_port);
+ } else if (description->handler != kHandlerNull) {
+ return false;
+ }
+
+ ExceptionPorts exception_ports(description->target_type, target_port);
+ if (!exception_ports.SetExceptionPort(description->mask,
+ service_port,
+ description->behavior,
+ description->flavor)) {
+ return false;
+ }
+
+ return true;
+}
+
+void Usage(const std::string& me) {
+ fprintf(stderr,
+"Usage: %s [OPTION]... [COMMAND [ARG]...]\n"
+"View and change Mach exception ports, and run COMMAND if supplied.\n"
+"\n"
+" -s, --set-handler=DESCRIPTION set an exception port to DESCRIPTION, see below\n"
+" --show-bootstrap=SERVICE look up and display a service registered with\n"
+" the bootstrap server\n"
+" -p, --pid=PID operate on PID instead of the current task\n"
+" -h, --show-host display original host exception ports\n"
+" -t, --show-task display original task exception ports\n"
+" --show-thread display original thread exception ports\n"
+" -H, --show-new-host display modified host exception ports\n"
+" -T, --show-new-task display modified task exception ports\n"
+" --show-new-thread display modified thread exception ports\n"
+" -n, --numeric display values numerically, not symbolically\n"
+" --help display this help and exit\n"
+" --version output version information and exit\n"
+"\n"
+"Any operations on host exception ports require superuser permissions.\n"
+"\n"
+"DESCRIPTION is formatted as a comma-separated sequence of tokens, where each\n"
+"token consists of a key and value separated by an equals sign. Available keys:\n"
+" target which target's exception ports to set: host, task, or thread\n"
+" mask the mask of exception types to handle: CRASH, ALL, or others\n"
+" behavior the specific exception handler routine to call: DEFAULT, STATE,\n"
+" or STATE_IDENTITY, possibly with MACH_EXCEPTION_CODES.\n"
+" flavor the thread state flavor passed to the handler: architecture-specific\n"
+" handler the exception handler: NULL or bootstrap:SERVICE, indicating that\n"
+" the handler should be looked up with the bootstrap server\n"
+"The default DESCRIPTION is\n"
+" target=task,mask=CRASH,behavior=DEFAULT|MACH,flavor=NONE,handler=NULL\n",
+ me.c_str());
+ ToolSupport::UsageTail(me);
+}
+
+int ExceptionPortToolMain(int argc, char* argv[]) {
+ const std::string me(basename(argv[0]));
+
+ enum ExitCode {
+ kExitSuccess = EXIT_SUCCESS,
+
+ // To differentiate this tool’s errors from errors in the programs it execs,
+ // use a high exit code for ordinary failures instead of EXIT_FAILURE. This
+ // is the same rationale for using the distinct exit codes for exec
+ // failures.
+ kExitFailure = 125,
+
+ // Like env, use exit code 126 if the program was found but could not be
+ // invoked, and 127 if it could not be found.
+ // http://pubs.opengroup.org/onlinepubs/9699919799/utilities/env.html
+ kExitExecFailure = 126,
+ kExitExecENOENT = 127,
+ };
+
+ enum OptionFlags {
+ // “Short” (single-character) options.
+ kOptionSetPort = 's',
+ kOptionPid = 'p',
+ kOptionShowHost = 'h',
+ kOptionShowTask = 't',
+ kOptionShowNewHost = 'H',
+ kOptionShowNewTask = 'T',
+ kOptionNumeric = 'n',
+
+ // Long options without short equivalents.
+ kOptionLastChar = 255,
+ kOptionShowBootstrap,
+ kOptionShowThread,
+ kOptionShowNewThread,
+
+ // Standard options.
+ kOptionHelp = -2,
+ kOptionVersion = -3,
+ };
+
+ struct {
+ std::vector<const char*> show_bootstrap;
+ std::vector<ExceptionHandlerDescription> set_handler;
+ pid_t pid;
+ task_t alternate_task;
+ bool show_host;
+ bool show_task;
+ bool show_thread;
+ bool show_new_host;
+ bool show_new_task;
+ bool show_new_thread;
+ bool numeric;
+ } options = {};
+
+ const option long_options[] = {
+ {"set-handler", required_argument, nullptr, kOptionSetPort},
+ {"show-bootstrap", required_argument, nullptr, kOptionShowBootstrap},
+ {"pid", required_argument, nullptr, kOptionPid},
+ {"show-host", no_argument, nullptr, kOptionShowHost},
+ {"show-task", no_argument, nullptr, kOptionShowTask},
+ {"show-thread", no_argument, nullptr, kOptionShowThread},
+ {"show-new-host", no_argument, nullptr, kOptionShowNewHost},
+ {"show-new-task", no_argument, nullptr, kOptionShowNewTask},
+ {"show-new-thread", no_argument, nullptr, kOptionShowNewThread},
+ {"numeric", no_argument, nullptr, kOptionNumeric},
+ {"help", no_argument, nullptr, kOptionHelp},
+ {"version", no_argument, nullptr, kOptionVersion},
+ {nullptr, 0, nullptr, 0},
+ };
+
+ int opt;
+ while ((opt = getopt_long(argc, argv, "+s:p:htHTn", long_options, nullptr)) !=
+ -1) {
+ switch (opt) {
+ case kOptionSetPort: {
+ options.set_handler.push_back({});
+ ExceptionHandlerDescription* description = &options.set_handler.back();
+ description->target_type = ExceptionPorts::kTargetTypeTask;
+ description->mask = EXC_MASK_CRASH;
+ description->behavior = EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES;
+ description->flavor = THREAD_STATE_NONE;
+ description->handler = "NULL";
+ if (!ParseHandlerString(optarg, description)) {
+ fprintf(stderr,
+ "%s: invalid exception handler: %s\n",
+ me.c_str(),
+ optarg);
+ return kExitFailure;
+ }
+ break;
+ }
+ case kOptionShowBootstrap:
+ options.show_bootstrap.push_back(optarg);
+ break;
+ case kOptionPid:
+ if (!StringToNumber(optarg, &options.pid)) {
+ fprintf(stderr, "%s: invalid pid: %s\n", me.c_str(), optarg);
+ return kExitFailure;
+ }
+ break;
+ case kOptionShowHost:
+ options.show_host = true;
+ break;
+ case kOptionShowTask:
+ options.show_task = true;
+ break;
+ case kOptionShowThread:
+ options.show_thread = true;
+ break;
+ case kOptionShowNewHost:
+ options.show_new_host = true;
+ break;
+ case kOptionShowNewTask:
+ options.show_new_task = true;
+ break;
+ case kOptionShowNewThread:
+ options.show_new_thread = true;
+ break;
+ case kOptionNumeric:
+ options.numeric = true;
+ break;
+ case kOptionHelp:
+ Usage(me);
+ return kExitSuccess;
+ case kOptionVersion:
+ ToolSupport::Version(me);
+ return kExitSuccess;
+ default:
+ ToolSupport::UsageHint(me, nullptr);
+ return kExitFailure;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (options.show_bootstrap.empty() && !options.show_host &&
+ !options.show_task && !options.show_thread &&
+ options.set_handler.empty() && argc == 0) {
+ ToolSupport::UsageHint(me, "nothing to do");
+ return kExitFailure;
+ }
+
+ base::mac::ScopedMachSendRight alternate_task_owner;
+ if (options.pid) {
+ if (argc) {
+ ToolSupport::UsageHint(me, "cannot combine -p with COMMAND");
+ return kExitFailure;
+ }
+
+ options.alternate_task = TaskForPID(options.pid);
+ if (options.alternate_task == TASK_NULL) {
+ return kExitFailure;
+ }
+ alternate_task_owner.reset(options.alternate_task);
+ }
+
+ // This tool may have been installed as a setuid binary so that TaskForPID()
+ // could succeed. Drop any privileges now that they’re no longer necessary.
+ DropPrivileges();
+
+ MachSendRightPool mach_send_right_pool;
+
+ // Show bootstrap services requested.
+ for (const char* service : options.show_bootstrap) {
+ ShowBootstrapService(service, &mach_send_right_pool);
+ }
+
+ // Show the original exception ports.
+ if (options.show_host) {
+ ShowExceptionPorts(
+ ExceptionPorts(ExceptionPorts::kTargetTypeHost, HOST_NULL),
+ options.numeric,
+ false,
+ &mach_send_right_pool);
+ }
+ if (options.show_task) {
+ ShowExceptionPorts(
+ ExceptionPorts(ExceptionPorts::kTargetTypeTask, options.alternate_task),
+ options.numeric,
+ false,
+ &mach_send_right_pool);
+ }
+ if (options.show_thread) {
+ ShowExceptionPorts(
+ ExceptionPorts(ExceptionPorts::kTargetTypeThread, THREAD_NULL),
+ options.numeric,
+ false,
+ &mach_send_right_pool);
+ }
+
+ if (!options.set_handler.empty()) {
+ // Set new exception handlers.
+ for (ExceptionHandlerDescription description : options.set_handler) {
+ if (!SetExceptionPort(
+ &description,
+ description.target_type == ExceptionPorts::kTargetTypeTask
+ ? options.alternate_task
+ : TASK_NULL)) {
+ return kExitFailure;
+ }
+ }
+
+ // Show changed exception ports.
+ if (options.show_new_host) {
+ ShowExceptionPorts(
+ ExceptionPorts(ExceptionPorts::kTargetTypeHost, HOST_NULL),
+ options.numeric,
+ true,
+ &mach_send_right_pool);
+ }
+ if (options.show_new_task) {
+ ShowExceptionPorts(
+ ExceptionPorts(ExceptionPorts::kTargetTypeTask,
+ options.alternate_task),
+ options.numeric,
+ true,
+ &mach_send_right_pool);
+ }
+ if (options.show_new_thread) {
+ ShowExceptionPorts(
+ ExceptionPorts(ExceptionPorts::kTargetTypeThread, THREAD_NULL),
+ options.numeric,
+ true,
+ &mach_send_right_pool);
+ }
+ }
+
+ if (argc) {
+ // Using the remaining arguments, start a new program with the new set of
+ // exception ports in effect.
+ execvp(argv[0], argv);
+ PLOG(ERROR) << "execvp " << argv[0];
+ return errno == ENOENT ? kExitExecENOENT : kExitExecFailure;
+ }
+
+ return kExitSuccess;
+}
+
+} // namespace
+} // namespace crashpad
+
+int main(int argc, char* argv[]) {
+ return crashpad::ExceptionPortToolMain(argc, argv);
+}
diff --git a/chromium/third_party/crashpad/crashpad/tools/mac/on_demand_service_tool.ad b/chromium/third_party/crashpad/crashpad/tools/mac/on_demand_service_tool.ad
new file mode 100644
index 00000000000..5db4713fd20
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/tools/mac/on_demand_service_tool.ad
@@ -0,0 +1,102 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+:doctype: manpage
+
+= on_demand_service_tool(1)
+
+== Name
+
+on_demand_service_tool - Load and unload on-demand Mach services registered with
+launchd(8)
+
+== Synopsis
+
+[verse]
+*on_demand_service_tool* *-L* *-l* 'LABEL' ['OPTION…'] 'COMMAND' ['ARG…']
+*on_demand_service_tool* *-U* *-l* 'LABEL'
+
+== Description
+
+On-demand services may be registered with launchd(8) by using the *--load* form.
+One or more service names may be registered with the bootstrap server by
+specifying *--mach-service*. When a Mach message is sent to any of these
+services, launchd(8) will invoke 'COMMAND' along with any arguments specified
+('ARG…'). 'COMMAND' must be an absolute pathname.
+
+The *--unload* form unregisters jobs registered with launchd(8).
+
+== Options
+
+*-L*, *--load*::
+Registers a job with launchd(8). *--label*='LABEL' and 'COMMAND' are required.
+This operation may also be referred to as “load” or “submit”.
+
+*-U*, *--unload*::
+Unregisters a job with launchd(8). *--label*='LABEL' is required. This operation
+may also be referred to as “unload” or “remove”.
+
+*-l*, *--label*='LABEL'::
+'LABEL' is used as the job label to identify the job to launchd(8). 'LABEL' must
+be unique within a launchd(8) context.
+
+*-m*, *--mach-service*='SERVICE'::
+In conjunction with *--load*, registers 'SERVICE' with the bootstrap server.
+Clients will be able to obtain a send right by looking up the 'SERVICE' name
+with the bootstrap server. When a message is sent to such a Mach port,
+launchd(8) will invoke 'COMMAND' along with any arguments specified ('ARG…') if
+it is not running. This forms the “on-demand” nature referenced by this tool’s
+name. This option may appear zero, one, or more times. 'SERVICE' must be unique
+within a bootstrap context.
+
+*--help*::
+Display help and exit.
+
+*--version*::
+Output version information and exit.
+
+== Examples
+
+Registers an on-demand server that will execute man_link:catch_exception_tool[1]
+when a Mach message is sent to a Mach port obtained from the bootstrap server by
+looking up the name +svc+:
+[subs="quotes"]
+----
+$ *on_demand_service_tool --load --label=catch_exception \
+ --mach-service=svc \
+ $(which catch_exception_tool) --mach-service=svc \
+ --file=/tmp/out --persistent --timeout=0*
+----
+
+Unregisters the on-demand server installed above:
+[subs="quotes"]
+----
+$ *on_demand_service_tool --unload --label=catch_exception*
+----
+
+== Exit Status
+
+*0*::
+Success.
+
+*1*::
+Failure, with a message printed to the standard error stream.
+
+== See Also
+
+man_link:catch_exception_tool[1],
+man_link:exception_port_tool[1],
+launchctl(1)
+
+include::../../doc/support/man_footer.ad[]
diff --git a/chromium/third_party/crashpad/crashpad/tools/mac/on_demand_service_tool.mm b/chromium/third_party/crashpad/crashpad/tools/mac/on_demand_service_tool.mm
new file mode 100644
index 00000000000..c6b69520099
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/tools/mac/on_demand_service_tool.mm
@@ -0,0 +1,197 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <CoreFoundation/CoreFoundation.h>
+#import <Foundation/Foundation.h>
+#include <getopt.h>
+#include <launch.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include "base/mac/foundation_util.h"
+#include "base/strings/sys_string_conversions.h"
+#include "tools/tool_support.h"
+#include "util/mac/service_management.h"
+#include "util/stdlib/objc.h"
+
+namespace crashpad {
+namespace {
+
+void Usage(const std::string& me) {
+ fprintf(stderr,
+"Usage: %s -L -l LABEL [OPTION]... COMMAND [ARG]...\n"
+" %s -U -l LABEL\n"
+"Load and unload on-demand Mach services from launchd.\n"
+"\n"
+" -L, --load load (submit) the job identified by --label;\n"
+" COMMAND must be specified\n"
+" -U, --unload unload (remove) the job identified by --label\n"
+" -l, --label=LABEL identify the job to launchd with LABEL\n"
+" -m, --mach-service=SERVICE register SERVICE with the bootstrap server\n"
+" --help display this help and exit\n"
+" --version output version information and exit\n",
+ me.c_str(),
+ me.c_str());
+ ToolSupport::UsageTail(me);
+}
+
+int OnDemandServiceToolMain(int argc, char* argv[]) {
+ const std::string me(basename(argv[0]));
+
+ enum Operation {
+ kOperationUnknown = 0,
+ kOperationLoadJob,
+ kOperationUnloadJob,
+ };
+
+ enum OptionFlags {
+ // “Short” (single-character) options.
+ kOptionLoadJob = 'L',
+ kOptionUnloadJob = 'U',
+ kOptionJobLabel = 'l',
+ kOptionMachService = 'm',
+
+ // Long options without short equivalents.
+ kOptionLastChar = 255,
+
+ // Standard options.
+ kOptionHelp = -2,
+ kOptionVersion = -3,
+ };
+
+ struct {
+ Operation operation;
+ std::string job_label;
+ std::vector<std::string> mach_services;
+ } options = {};
+
+ const option long_options[] = {
+ {"load", no_argument, nullptr, kOptionLoadJob},
+ {"unload", no_argument, nullptr, kOptionUnloadJob},
+ {"label", required_argument, nullptr, kOptionJobLabel},
+ {"mach-service", required_argument, nullptr, kOptionMachService},
+ {"help", no_argument, nullptr, kOptionHelp},
+ {"version", no_argument, nullptr, kOptionVersion},
+ {nullptr, 0, nullptr, 0},
+ };
+
+ int opt;
+ while ((opt = getopt_long(argc, argv, "+LUl:m:", long_options, nullptr)) !=
+ -1) {
+ switch (opt) {
+ case kOptionLoadJob:
+ options.operation = kOperationLoadJob;
+ break;
+ case kOptionUnloadJob:
+ options.operation = kOperationUnloadJob;
+ break;
+ case kOptionJobLabel:
+ options.job_label = optarg;
+ break;
+ case kOptionMachService:
+ options.mach_services.push_back(optarg);
+ break;
+ case kOptionHelp:
+ Usage(me);
+ return EXIT_SUCCESS;
+ case kOptionVersion:
+ ToolSupport::Version(me);
+ return EXIT_SUCCESS;
+ default:
+ ToolSupport::UsageHint(me, nullptr);
+ return EXIT_FAILURE;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (options.job_label.empty()) {
+ ToolSupport::UsageHint(me, "must provide -l");
+ return EXIT_FAILURE;
+ }
+
+ switch (options.operation) {
+ case kOperationLoadJob: {
+ if (argc == 0) {
+ ToolSupport::UsageHint(me, "must provide COMMAND with -L");
+ return EXIT_FAILURE;
+ }
+
+ @autoreleasepool {
+ NSString* job_label = base::SysUTF8ToNSString(options.job_label);
+
+ NSMutableArray* command = [NSMutableArray arrayWithCapacity:argc];
+ for (int index = 0; index < argc; ++index) {
+ NSString* argument = base::SysUTF8ToNSString(argv[index]);
+ [command addObject:argument];
+ }
+
+ NSDictionary* job_dictionary = @{
+ @LAUNCH_JOBKEY_LABEL : job_label,
+ @LAUNCH_JOBKEY_PROGRAMARGUMENTS : command,
+ };
+
+ if (!options.mach_services.empty()) {
+ NSMutableDictionary* mach_services = [NSMutableDictionary
+ dictionaryWithCapacity:options.mach_services.size()];
+ for (std::string mach_service : options.mach_services) {
+ NSString* mach_service_ns = base::SysUTF8ToNSString(mach_service);
+ [mach_services setObject:@YES forKey:mach_service_ns];
+ }
+
+ NSMutableDictionary* mutable_job_dictionary =
+ [[job_dictionary mutableCopy] autorelease];
+ [mutable_job_dictionary setObject:mach_services
+ forKey:@LAUNCH_JOBKEY_MACHSERVICES];
+ job_dictionary = mutable_job_dictionary;
+ }
+
+ CFDictionaryRef job_dictionary_cf =
+ base::mac::NSToCFCast(job_dictionary);
+ if (!ServiceManagementSubmitJob(job_dictionary_cf)) {
+ fprintf(stderr, "%s: failed to submit job\n", me.c_str());
+ return EXIT_FAILURE;
+ }
+ }
+
+ return EXIT_SUCCESS;
+ }
+
+ case kOperationUnloadJob: {
+ if (!ServiceManagementRemoveJob(options.job_label, true)) {
+ fprintf(stderr, "%s: failed to remove job\n", me.c_str());
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+ }
+
+ default: {
+ ToolSupport::UsageHint(me, "must provide -L or -U");
+ return EXIT_FAILURE;
+ }
+ }
+}
+
+} // namespace
+} // namespace crashpad
+
+int main(int argc, char* argv[]) {
+ return crashpad::OnDemandServiceToolMain(argc, argv);
+}
diff --git a/chromium/third_party/crashpad/crashpad/tools/mac/run_with_crashpad.ad b/chromium/third_party/crashpad/crashpad/tools/mac/run_with_crashpad.ad
new file mode 100644
index 00000000000..129488a3113
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/tools/mac/run_with_crashpad.ad
@@ -0,0 +1,112 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+:doctype: manpage
+
+= run_with_crashpad(1)
+
+== Name
+
+run_with_crashpad - Run a program with a Crashpad exception handler
+
+== Synopsis
+
+[verse]
+*run_with_crashpad* ['OPTION…'] 'COMMAND' ['ARG…']
+
+== Description
+
+Starts a Crashpad exception handler server such as crashpad_handler(8) and
+becomes its client, setting an exception port referencing the handler. Then,
+executes 'COMMAND' along with any arguments specified ('ARG…') with the new
+exception port in effect.
+
+The exception port is configured to receive exceptions of type +EXC_CRASH+,
++EXC_RESOURCE+, and +EXC_GUARD+. The exception behavior is configured as
++EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES+. The thread state flavor is
+set to +MACHINE_THREAD_STATE+.
+
+Programs that use the Crashpad client library directly will not normally use
+this tool. This tool exists to allow programs that are unaware of Crashpad to be
+run with a Crashpad exception handler.
+
+== Options
+*-h*, *--handler*='HANDLER'::
+Invoke 'HANDLER' as the Crashpad handler program instead of the default,
+*crashpad_handler*.
+
+*--annotation*='KEY=VALUE'::
+Passed to the Crashpad handler program as an *--annotation* argument.
+
+*--database*='PATH'::
+Passed to the Crashpad handler program as its *--database* argument.
+
+*--url*='URL'::
+Passed to the Crashpad handler program as its *--url* argument.
+
+*-a*, *--argument*='ARGUMENT'::
+Invokes the Crashpad handler program with 'ARGUMENT' as one of its arguments.
+This option may appear zero, one, or more times. If this program has a specific
+option such as *--database* matching the desired Crashpad handler program
+option, the specific option should be used in preference to *--argument*.
+Regardless of this option’s presence, the handler will always be invoked with
+the necessary arguments to perform a handshake.
+
+*--help*::
+Display help and exit.
+
+*--version*::
+Output version information and exit.
+
+== Examples
+
+Starts a Crashpad exception handler server by its default name,
+*crashpad_handler*, and runs a program with this handler in effect.
+[subs="quotes"]
+----
+$ *run_with_crashpad --database=/tmp/crashpad_database crash*
+Illegal instruction: 4
+----
+
+Starts a Crashpad exception handler server at a nonstandard path, and runs
+man_link:exception_port_tool[1] to show the task-level exception ports.
+[subs="quotes"]
+----
+$ *run_with_crashpad --handler=/tmp/crashpad_handler \
+ --database=/tmp/crashpad_database exception_port_tool \
+ --show-task*
+task exception port 0, mask 0x1c00 (CRASH|RESOURCE|GUARD), port
+0x30b, behavior 0x80000003 (STATE_IDENTITY|MACH), flavor 7 (THREAD)
+----
+
+== Exit Status
+
+*0*::
+Success.
+
+*125*::
+Failure, with a message printed to the standard error stream.
+
+*126*::
+The program specified by 'COMMAND' was found, but could not be invoked.
+
+*127*::
+The program specified by 'COMMAND' could not be found.
+
+== See Also
+
+man_link:crashpad_handler[8],
+man_link:exception_port_tool[1]
+
+include::../../doc/support/man_footer.ad[]
diff --git a/chromium/third_party/crashpad/crashpad/tools/mac/run_with_crashpad.cc b/chromium/third_party/crashpad/crashpad/tools/mac/run_with_crashpad.cc
new file mode 100644
index 00000000000..0b4653aa20c
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/tools/mac/run_with_crashpad.cc
@@ -0,0 +1,189 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <errno.h>
+#include <getopt.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "client/crashpad_client.h"
+#include "tools/tool_support.h"
+#include "util/stdlib/map_insert.h"
+#include "util/string/split_string.h"
+
+namespace crashpad {
+namespace {
+
+void Usage(const std::string& me) {
+ fprintf(stderr,
+"Usage: %s [OPTION]... COMMAND [ARG]...\n"
+"Start a Crashpad handler and have it handle crashes from COMMAND.\n"
+"\n"
+" -h, --handler=HANDLER invoke HANDLER instead of crashpad_handler\n"
+" --annotation=KEY=VALUE passed to the handler as an --annotation argument\n"
+" --database=PATH passed to the handler as its --database argument\n"
+" --url=URL passed to the handler as its --url argument\n"
+" -a, --argument=ARGUMENT invoke the handler with ARGUMENT\n"
+" --help display this help and exit\n"
+" --version output version information and exit\n",
+ me.c_str());
+ ToolSupport::UsageTail(me);
+}
+
+int RunWithCrashpadMain(int argc, char* argv[]) {
+ const std::string me(basename(argv[0]));
+
+ enum ExitCode {
+ kExitSuccess = EXIT_SUCCESS,
+
+ // To differentiate this tool’s errors from errors in the programs it execs,
+ // use a high exit code for ordinary failures instead of EXIT_FAILURE. This
+ // is the same rationale for using the distinct exit codes for exec
+ // failures.
+ kExitFailure = 125,
+
+ // Like env, use exit code 126 if the program was found but could not be
+ // invoked, and 127 if it could not be found.
+ // http://pubs.opengroup.org/onlinepubs/9699919799/utilities/env.html
+ kExitExecFailure = 126,
+ kExitExecENOENT = 127,
+ };
+
+ enum OptionFlags {
+ // “Short” (single-character) options.
+ kOptionHandler = 'h',
+ kOptionArgument = 'a',
+
+ // Long options without short equivalents.
+ kOptionLastChar = 255,
+ kOptionAnnotation,
+ kOptionDatabase,
+ kOptionURL,
+
+ // Standard options.
+ kOptionHelp = -2,
+ kOptionVersion = -3,
+ };
+
+ const option long_options[] = {
+ {"handler", required_argument, nullptr, kOptionHandler},
+ {"annotation", required_argument, nullptr, kOptionAnnotation},
+ {"database", required_argument, nullptr, kOptionDatabase},
+ {"url", required_argument, nullptr, kOptionURL},
+ {"argument", required_argument, nullptr, kOptionArgument},
+ {"help", no_argument, nullptr, kOptionHelp},
+ {"version", no_argument, nullptr, kOptionVersion},
+ {nullptr, 0, nullptr, 0},
+ };
+
+ struct {
+ std::string handler;
+ std::map<std::string, std::string> annotations;
+ std::string database;
+ std::string url;
+ std::vector<std::string> arguments;
+ } options = {};
+ options.handler = "crashpad_handler";
+
+ int opt;
+ while ((opt = getopt_long(argc, argv, "+a:h:", long_options, nullptr)) !=
+ -1) {
+ switch (opt) {
+ case kOptionHandler: {
+ options.handler = optarg;
+ break;
+ }
+ case kOptionAnnotation: {
+ std::string key;
+ std::string value;
+ if (!SplitString(optarg, '=', &key, &value)) {
+ ToolSupport::UsageHint(me, "--annotation requires KEY=VALUE");
+ return EXIT_FAILURE;
+ }
+ std::string old_value;
+ if (!MapInsertOrReplace(&options.annotations, key, value, &old_value)) {
+ LOG(WARNING) << "duplicate key " << key << ", discarding value "
+ << old_value;
+ }
+ break;
+ }
+ case kOptionDatabase: {
+ options.database = optarg;
+ break;
+ }
+ case kOptionURL: {
+ options.url = optarg;
+ break;
+ }
+ case kOptionArgument: {
+ options.arguments.push_back(optarg);
+ break;
+ }
+ case kOptionHelp: {
+ Usage(me);
+ return kExitSuccess;
+ }
+ case kOptionVersion: {
+ ToolSupport::Version(me);
+ return kExitSuccess;
+ }
+ default: {
+ ToolSupport::UsageHint(me, nullptr);
+ return kExitFailure;
+ }
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!argc) {
+ ToolSupport::UsageHint(me, "COMMAND is required");
+ return kExitFailure;
+ }
+
+ // Start the handler process and direct exceptions to it.
+ CrashpadClient crashpad_client;
+ if (!crashpad_client.StartHandler(base::FilePath(options.handler),
+ base::FilePath(options.database),
+ options.url,
+ options.annotations,
+ options.arguments)) {
+ return kExitFailure;
+ }
+
+ if (!crashpad_client.UseHandler()) {
+ return kExitFailure;
+ }
+
+ // Using the remaining arguments, start a new program with the new exception
+ // port in effect.
+ execvp(argv[0], argv);
+ PLOG(ERROR) << "execvp " << argv[0];
+ return errno == ENOENT ? kExitExecENOENT : kExitExecFailure;
+}
+
+} // namespace
+} // namespace crashpad
+
+int main(int argc, char* argv[]) {
+ return crashpad::RunWithCrashpadMain(argc, argv);
+}
diff --git a/chromium/third_party/crashpad/crashpad/tools/mac/sectaskaccess_info.plist b/chromium/third_party/crashpad/crashpad/tools/mac/sectaskaccess_info.plist
new file mode 100644
index 00000000000..faebead920d
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/tools/mac/sectaskaccess_info.plist
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>SecTaskAccess</key>
+ <array>
+ <string>allowed</string>
+ </array>
+</dict>
+</plist>
diff --git a/chromium/third_party/crashpad/crashpad/tools/tool_support.cc b/chromium/third_party/crashpad/crashpad/tools/tool_support.cc
new file mode 100644
index 00000000000..5d3592bcfdf
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/tools/tool_support.cc
@@ -0,0 +1,101 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "tools/tool_support.h"
+
+#include <stdio.h>
+
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/utf_string_conversions.h"
+#include "package.h"
+
+namespace crashpad {
+
+// static
+void ToolSupport::Version(const base::FilePath& me) {
+ fprintf(stderr,
+ "%" PRFilePath " (%s) %s\n%s\n",
+ me.value().c_str(),
+ PACKAGE_NAME,
+ PACKAGE_VERSION,
+ PACKAGE_COPYRIGHT);
+}
+
+// static
+void ToolSupport::UsageTail(const base::FilePath& me) {
+ fprintf(stderr,
+ "\nReport %" PRFilePath " bugs to\n%s\n%s home page: <%s>\n",
+ me.value().c_str(),
+ PACKAGE_BUGREPORT,
+ PACKAGE_NAME,
+ PACKAGE_URL);
+}
+
+// static
+void ToolSupport::UsageHint(const base::FilePath& me, const char* hint) {
+ if (hint) {
+ fprintf(stderr, "%" PRFilePath ": %s\n", me.value().c_str(), hint);
+ }
+ fprintf(stderr,
+ "Try '%" PRFilePath " --help' for more information.\n",
+ me.value().c_str());
+}
+
+#if defined(OS_POSIX)
+// static
+void ToolSupport::Version(const std::string& me) {
+ Version(base::FilePath(me));
+}
+
+// static
+void ToolSupport::UsageTail(const std::string& me) {
+ UsageTail(base::FilePath(me));
+}
+
+// static
+void ToolSupport::UsageHint(const std::string& me, const char* hint) {
+ UsageHint(base::FilePath(me), hint);
+}
+#endif // OS_POSIX
+
+#if defined(OS_WIN)
+
+// static
+int ToolSupport::Wmain(int argc, wchar_t* argv[], int (*entry)(int, char* [])) {
+ scoped_ptr<char* []> argv_as_utf8(new char* [argc + 1]);
+ std::vector<std::string> storage;
+ storage.reserve(argc);
+ for (int i = 0; i < argc; ++i) {
+ storage.push_back(base::UTF16ToUTF8(argv[i]));
+ argv_as_utf8[i] = &storage[i][0];
+ }
+ argv_as_utf8[argc] = nullptr;
+ return entry(argc, argv_as_utf8.get());
+}
+
+#endif // OS_WIN
+
+// static
+base::FilePath::StringType ToolSupport::CommandLineArgumentToFilePathStringType(
+ const base::StringPiece& path) {
+#if defined(OS_POSIX)
+ return path.as_string();
+#elif defined(OS_WIN)
+ return base::UTF8ToUTF16(path);
+#endif // OS_POSIX
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/tools/tool_support.h b/chromium/third_party/crashpad/crashpad/tools/tool_support.h
new file mode 100644
index 00000000000..9099691a209
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/tools/tool_support.h
@@ -0,0 +1,82 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_TOOLS_TOOL_SUPPORT_H_
+#define CRASHPAD_TOOLS_TOOL_SUPPORT_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/strings/string_piece.h"
+#include "build/build_config.h"
+
+namespace crashpad {
+
+//! \brief Common functions used by command line tools.
+class ToolSupport {
+ public:
+ //! \brief Handles `--version`.
+ //!
+ //! \param[in] me The tool’s name, the basename of `argv[0]`.
+ static void Version(const base::FilePath& me);
+
+ //! \brief Prints the footer for `--help`.
+ //!
+ //! \param[in] me The tool’s name, the basename of `argv[0]`.
+ static void UsageTail(const base::FilePath& me);
+
+ //! \brief Suggests using `--help` when a command line tool can’t make sense
+ //! of its arguments.
+ //!
+ //! \param[in] me The tool’s name, the basename of `argv[0]`.
+ static void UsageHint(const base::FilePath& me, const char* hint);
+
+#if defined(OS_POSIX) || DOXYGEN
+ //! \copydoc Version
+ static void Version(const std::string& me);
+
+ //! \copydoc UsageTail
+ static void UsageTail(const std::string& me);
+
+ //! \copydoc UsageHint
+ static void UsageHint(const std::string& me, const char* hint);
+#endif // OS_POSIX
+
+#if defined(OS_WIN) || DOXYGEN
+ //! \brief Converts \a argv `wchar_t` UTF-16 to UTF-8, and passes onwards to a
+ //! UTF-8 entry point.
+ //!
+ //! \return The return value of \a entry.
+ static int Wmain(int argc, wchar_t* argv[], int (*entry)(int, char*[]));
+#endif // OS_WIN
+
+ //! \brief Converts a command line argument to the string type suitable for
+ //! base::FilePath.
+ //!
+ //! On POSIX, this is a no-op. On Windows, assumes that Wmain() was used, and
+ //! the input argument was converted from UTF-16 in a `wchar_t*` to UTF-8 in a
+ //! `char*`. This undoes that transformation.
+ //!
+ //! \sa Wmain()
+ static base::FilePath::StringType CommandLineArgumentToFilePathStringType(
+ const base::StringPiece& arg);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ToolSupport);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_TOOLS_TOOL_SUPPORT_H_
diff --git a/chromium/third_party/crashpad/crashpad/tools/tools.gyp b/chromium/third_party/crashpad/crashpad/tools/tools.gyp
new file mode 100644
index 00000000000..4876300538f
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/tools/tools.gyp
@@ -0,0 +1,193 @@
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'includes': [
+ '../build/crashpad.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'crashpad_tool_support',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../third_party/mini_chromium/mini_chromium.gyp:base',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'tool_support.cc',
+ 'tool_support.h',
+ ],
+ },
+ {
+ 'target_name': 'crashpad_database_util',
+ 'type': 'executable',
+ 'dependencies': [
+ 'crashpad_tool_support',
+ '../client/client.gyp:crashpad_client',
+ '../compat/compat.gyp:crashpad_compat',
+ '../third_party/mini_chromium/mini_chromium.gyp:base',
+ '../util/util.gyp:crashpad_util',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'crashpad_database_util.cc',
+ ],
+ },
+ {
+ 'target_name': 'generate_dump',
+ 'type': 'executable',
+ 'dependencies': [
+ 'crashpad_tool_support',
+ '../compat/compat.gyp:crashpad_compat',
+ '../minidump/minidump.gyp:crashpad_minidump',
+ '../snapshot/snapshot.gyp:crashpad_snapshot',
+ '../third_party/mini_chromium/mini_chromium.gyp:base',
+ '../util/util.gyp:crashpad_util',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'generate_dump.cc',
+ ],
+ 'conditions': [
+ ['OS=="mac"', {
+ 'xcode_settings': {
+ 'OTHER_LDFLAGS': [
+ '-sectcreate',
+ '__TEXT',
+ '__info_plist',
+ '<(sectaskaccess_info_plist)'
+ ],
+ },
+ }],
+ ],
+ }
+ ],
+ 'conditions': [
+ ['OS=="mac"', {
+ 'variables': {
+ # Programs that use task_for_pid() can indicate to taskgated(8) in their
+ # Info.plist that they are allowed to call that function. In order for
+ # this to work, the programs in question must be signed by an authority
+ # trusted by the system. Signing is beyond the scope of the build, but
+ # the key to make this work is placed in Info.plist to enable the
+ # desired behavior once the tools that require this access are signed.
+ #
+ # The tools built here are flat-file executables, and are not bundled.
+ # To have an Info.plist, they must have a special __TEXT,__info_plist
+ # section. This section is created at link time.
+ #
+ # The Info.plist for this purpose is mac/sectaskaccess_info.plist and is
+ # referenced by OTHER_LDFLAGS. ninja runs the link step from the output
+ # directory such as out/Release, and requires a relative path from that
+ # directory. Xcode runs the link step from the directory of the
+ # .xcodeproj, which is the directory of the .gyp file.
+ 'conditions': [
+ ['GENERATOR=="ninja"', {
+ 'sectaskaccess_info_plist': '<!(pwd)/mac/sectaskaccess_info.plist',
+ }, { # else: GENERATOR!="ninja"
+ 'sectaskaccess_info_plist': 'mac/sectaskaccess_info.plist',
+ }],
+ ],
+ },
+
+ 'targets': [
+ {
+ 'target_name': 'catch_exception_tool',
+ 'type': 'executable',
+ 'dependencies': [
+ 'crashpad_tool_support',
+ '../compat/compat.gyp:crashpad_compat',
+ '../third_party/mini_chromium/mini_chromium.gyp:base',
+ '../util/util.gyp:crashpad_util',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'mac/catch_exception_tool.cc',
+ ],
+ },
+ {
+ 'target_name': 'exception_port_tool',
+ 'type': 'executable',
+ 'dependencies': [
+ 'crashpad_tool_support',
+ '../compat/compat.gyp:crashpad_compat',
+ '../third_party/mini_chromium/mini_chromium.gyp:base',
+ '../util/util.gyp:crashpad_util',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'mac/exception_port_tool.cc',
+ ],
+ 'xcode_settings': {
+ 'OTHER_LDFLAGS': [
+ '-sectcreate',
+ '__TEXT',
+ '__info_plist',
+ '<(sectaskaccess_info_plist)'
+ ],
+ },
+ },
+ {
+ 'target_name': 'on_demand_service_tool',
+ 'type': 'executable',
+ 'dependencies': [
+ 'crashpad_tool_support',
+ '../compat/compat.gyp:crashpad_compat',
+ '../third_party/mini_chromium/mini_chromium.gyp:base',
+ '../util/util.gyp:crashpad_util',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/CoreFoundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ ],
+ },
+ 'sources': [
+ 'mac/on_demand_service_tool.mm',
+ ],
+ },
+ {
+ 'target_name': 'run_with_crashpad',
+ 'type': 'executable',
+ 'dependencies': [
+ 'crashpad_tool_support',
+ '../client/client.gyp:crashpad_client',
+ '../compat/compat.gyp:crashpad_compat',
+ '../third_party/mini_chromium/mini_chromium.gyp:base',
+ '../util/util.gyp:crashpad_util',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'mac/run_with_crashpad.cc',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/chromium/third_party/crashpad/crashpad/util/file/file_io.cc b/chromium/third_party/crashpad/crashpad/util/file/file_io.cc
new file mode 100644
index 00000000000..6f2d0b58811
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/file/file_io.cc
@@ -0,0 +1,74 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/file/file_io.h"
+
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+
+namespace crashpad {
+
+bool LoggingReadFile(FileHandle file, void* buffer, size_t size) {
+ ssize_t expect = base::checked_cast<ssize_t>(size);
+ ssize_t rv = ReadFile(file, buffer, size);
+ if (rv < 0) {
+ PLOG(ERROR) << "read";
+ return false;
+ }
+ if (rv != expect) {
+ LOG(ERROR) << "read: expected " << expect << ", observed " << rv;
+ return false;
+ }
+
+ return true;
+}
+
+bool LoggingWriteFile(FileHandle file, const void* buffer, size_t size) {
+ ssize_t expect = base::checked_cast<ssize_t>(size);
+ ssize_t rv = WriteFile(file, buffer, size);
+ if (rv < 0) {
+ PLOG(ERROR) << "write";
+ return false;
+ }
+ if (rv != expect) {
+ LOG(ERROR) << "write: expected " << expect << ", observed " << rv;
+ return false;
+ }
+
+ return true;
+}
+
+void CheckedReadFile(FileHandle file, void* buffer, size_t size) {
+ CHECK(LoggingReadFile(file, buffer, size));
+}
+
+void CheckedWriteFile(FileHandle file, const void* buffer, size_t size) {
+ CHECK(LoggingWriteFile(file, buffer, size));
+}
+
+void CheckedReadFileAtEOF(FileHandle file) {
+ char c;
+ ssize_t rv = ReadFile(file, &c, 1);
+ if (rv < 0) {
+ PCHECK(rv == 0) << "read";
+ } else {
+ CHECK_EQ(rv, 0) << "read";
+ }
+}
+
+void CheckedCloseFile(FileHandle file) {
+ CHECK(LoggingCloseFile(file));
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/file/file_io.h b/chromium/third_party/crashpad/crashpad/util/file/file_io.h
new file mode 100644
index 00000000000..c3120c96398
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/file/file_io.h
@@ -0,0 +1,284 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_FILE_FILE_IO_H_
+#define CRASHPAD_UTIL_FILE_FILE_IO_H_
+
+#include <sys/types.h>
+
+#include "build/build_config.h"
+
+#if defined(OS_POSIX)
+#include "base/files/scoped_file.h"
+#elif defined(OS_WIN)
+#include <windows.h>
+#include "util/win/scoped_handle.h"
+#endif
+
+namespace base {
+class FilePath;
+} // namespace base
+
+namespace crashpad {
+
+#if defined(OS_POSIX) || DOXYGEN
+
+//! \brief Platform-specific alias for a low-level file handle.
+using FileHandle = int;
+
+//! \brief Platform-specific alias for a position in an open file.
+using FileOffset = off_t;
+
+//! \brief Scoped wrapper of a FileHandle.
+using ScopedFileHandle = base::ScopedFD;
+
+//! \brief A value that can never be a valid FileHandle.
+const FileHandle kInvalidFileHandle = -1;
+
+#elif defined(OS_WIN)
+
+using FileHandle = HANDLE;
+using FileOffset = LONGLONG;
+using ScopedFileHandle = ScopedFileHANDLE;
+
+const FileHandle kInvalidFileHandle = INVALID_HANDLE_VALUE;
+
+#endif
+
+//! \brief Determines the mode that LoggingOpenFileForWrite() uses.
+enum class FileWriteMode {
+ //! \brief Opens the file if it exists, or creates a new file.
+ kReuseOrCreate,
+
+ //! \brief Creates a new file. If the file already exists, it will be
+ //! overwritten.
+ kTruncateOrCreate,
+
+ //! \brief Creates a new file. If the file already exists, the open will fail.
+ kCreateOrFail,
+};
+
+//! \brief Determines the permissions bits for files created on POSIX systems.
+enum class FilePermissions : bool {
+ //! \brief Equivalent to `0600`.
+ kOwnerOnly,
+
+ //! \brief Equivalent to `0644`.
+ kWorldReadable,
+};
+
+//! \brief Determines the locking mode that LoggingLockFile() uses.
+enum class FileLocking : bool {
+ //! \brief Equivalent to `flock()` with `LOCK_SH`.
+ kShared,
+
+ //! \brief Equivalent to `flock()` with `LOCK_EX`.
+ kExclusive,
+};
+
+//! \brief Reads from a file, retrying when interrupted on POSIX or following a
+//! short read.
+//!
+//! This function reads into \a buffer, stopping only when \a size bytes have
+//! been read or when end-of-file has been reached. On Windows, reading from
+//! sockets is not currently supported.
+//!
+//! \return The number of bytes read and placed into \a buffer, or `-1` on
+//! error, with `errno` or `GetLastError()` set appropriately. On error, a
+//! portion of \a file may have been read into \a buffer.
+//!
+//! \sa WriteFile
+//! \sa LoggingReadFile
+//! \sa CheckedReadFile
+//! \sa CheckedReadFileAtEOF
+ssize_t ReadFile(FileHandle file, void* buffer, size_t size);
+
+//! \brief Writes to a file, retrying when interrupted or following a short
+//! write on POSIX.
+//!
+//! This function writes to \a file, stopping only when \a size bytes have been
+//! written.
+//!
+//! \return The number of bytes written from \a buffer, or `-1` on error, with
+//! `errno` or `GetLastError()` set appropriately. On error, a portion of
+//! \a buffer may have been written to \a file.
+//!
+//! \sa ReadFile
+//! \sa LoggingWriteFile
+//! \sa CheckedWriteFile
+ssize_t WriteFile(FileHandle file, const void* buffer, size_t size);
+
+//! \brief Wraps ReadFile(), ensuring that exactly \a size bytes are read.
+//!
+//! \return `true` on success. If \a size is out of the range of possible
+//! ReadFile() return values, if the underlying ReadFile() fails, or if
+//! other than \a size bytes were read, this function logs a message and
+//! returns `false`.
+//!
+//! \sa LoggingWriteFile
+//! \sa ReadFile
+//! \sa CheckedReadFile
+//! \sa CheckedReadFileAtEOF
+bool LoggingReadFile(FileHandle file, void* buffer, size_t size);
+
+//! \brief Wraps WriteFile(), ensuring that exactly \a size bytes are written.
+//!
+//! \return `true` on success. If \a size is out of the range of possible
+//! WriteFile() return values, if the underlying WriteFile() fails, or if
+//! other than \a size bytes were written, this function logs a message and
+//! returns `false`.
+//!
+//! \sa LoggingReadFile
+//! \sa WriteFile
+//! \sa CheckedWriteFile
+bool LoggingWriteFile(FileHandle file, const void* buffer, size_t size);
+
+//! \brief Wraps ReadFile(), ensuring that exactly \a size bytes are read.
+//!
+//! If \a size is out of the range of possible ReadFile() return values, if the
+//! underlying ReadFile() fails, or if other than \a size bytes were read, this
+//! function causes execution to terminate without returning.
+//!
+//! \sa CheckedWriteFile
+//! \sa ReadFile
+//! \sa LoggingReadFile
+//! \sa CheckedReadFileAtEOF
+void CheckedReadFile(FileHandle file, void* buffer, size_t size);
+
+//! \brief Wraps WriteFile(), ensuring that exactly \a size bytes are written.
+//!
+//! If \a size is out of the range of possible WriteFile() return values, if the
+//! underlying WriteFile() fails, or if other than \a size bytes were written,
+//! this function causes execution to terminate without returning.
+//!
+//! \sa CheckedReadFile
+//! \sa WriteFile
+//! \sa LoggingWriteFile
+void CheckedWriteFile(FileHandle file, const void* buffer, size_t size);
+
+//! \brief Wraps ReadFile(), ensuring that it indicates end-of-file.
+//!
+//! Attempts to read a single byte from \a file, expecting no data to be read.
+//! If the underlying ReadFile() fails, or if a byte actually is read, this
+//! function causes execution to terminate without returning.
+//!
+//! \sa CheckedReadFile
+//! \sa ReadFile
+void CheckedReadFileAtEOF(FileHandle file);
+
+//! \brief Wraps `open()` or `CreateFile()`, opening an existing file for
+//! reading. Logs an error if the operation fails.
+//!
+//! \return The newly opened FileHandle, or an invalid FileHandle on failure.
+//!
+//! \sa ScopedFileHandle
+FileHandle LoggingOpenFileForRead(const base::FilePath& path);
+
+//! \brief Wraps `open()` or `CreateFile()`, creating a file for output. Logs
+//! an error if the operation fails.
+//!
+//! \a mode determines the style (truncate, reuse, etc.) that is used to open
+//! the file. On POSIX, \a permissions determines the value that is passed as
+//! `mode` to `open()`. On Windows, the file is always opened in binary mode
+//! (that is, no CRLF translation). On Windows, the file is opened for sharing,
+//! see LoggingLockFile() and LoggingUnlockFile() to control concurrent access.
+//!
+//! \return The newly opened FileHandle, or an invalid FileHandle on failure.
+//!
+//! \sa FileWriteMode
+//! \sa FilePermissions
+//! \sa ScopedFileHandle
+FileHandle LoggingOpenFileForWrite(const base::FilePath& path,
+ FileWriteMode mode,
+ FilePermissions permissions);
+
+//! \brief Wraps `open()` or `CreateFile()`, creating a file for both input and
+//! output. Logs an error if the operation fails.
+//!
+//! \a mode determines the style (truncate, reuse, etc.) that is used to open
+//! the file. On POSIX, \a permissions determines the value that is passed as
+//! `mode` to `open()`. On Windows, the file is always opened in binary mode
+//! (that is, no CRLF translation). On Windows, the file is opened for sharing,
+//! see LoggingLockFile() and LoggingUnlockFile() to control concurrent access.
+//!
+//! \return The newly opened FileHandle, or an invalid FileHandle on failure.
+//!
+//! \sa FileWriteMode
+//! \sa FilePermissions
+//! \sa ScopedFileHandle
+FileHandle LoggingOpenFileForReadAndWrite(const base::FilePath& path,
+ FileWriteMode mode,
+ FilePermissions permissions);
+
+//! \brief Locks the given \a file using `flock()` on POSIX or `LockFileEx()` on
+//! Windows.
+//!
+//! It is an error to attempt to lock a file in a different mode when it is
+//! already locked. This call will block until the lock is acquired. The
+//! entire file is locked.
+//!
+//! If \a locking is FileLocking::kShared, \a file must have been opened for
+//! reading, and if it's FileLocking::kExclusive, \a file must have been opened
+//! for writing.
+//!
+//! \param[in] file The open file handle to be locked.
+//! \param[in] locking Controls whether the lock is a shared reader lock, or an
+//! exclusive writer lock.
+//!
+//! \return `true` on success, or `false` and a message will be logged.
+bool LoggingLockFile(FileHandle file, FileLocking locking);
+
+//! \brief Unlocks a file previously locked with LoggingLockFile().
+//!
+//! It is an error to attempt to unlock a file that was not previously locked.
+//! A previously-locked file should be unlocked before closing the file handle,
+//! otherwise on some OSs the lock may not be released immediately.
+//!
+//! \param[in] file The open locked file handle to be unlocked.
+//!
+//! \return `true` on success, or `false` and a message will be logged.
+bool LoggingUnlockFile(FileHandle file);
+
+//! \brief Wraps `lseek()` or `SetFilePointerEx()`. Logs an error if the
+//! operation fails.
+//!
+//! Repositions the offset of the open \a file to the specified \a offset,
+//! relative to \a whence. \a whence must be one of `SEEK_SET`, `SEEK_CUR`, or
+//! `SEEK_END`, and is interpreted in the usual way.
+//!
+//! \return The resulting offset in bytes from the beginning of the file, or
+//! `-1` on failure.
+FileOffset LoggingSeekFile(FileHandle file, FileOffset offset, int whence);
+
+//! \brief Truncates the given \a file to zero bytes in length.
+//!
+//! \return `true` on success, or `false`, and a message will be logged.
+bool LoggingTruncateFile(FileHandle file);
+
+//! \brief Wraps `close()` or `CloseHandle()`, logging an error if the operation
+//! fails.
+//!
+//! \return On success, `true` is returned. On failure, an error is logged and
+//! `false` is returned.
+bool LoggingCloseFile(FileHandle file);
+
+//! \brief Wraps `close()` or `CloseHandle()`, ensuring that it succeeds.
+//!
+//! If the underlying function fails, this function causes execution to
+//! terminate without returning.
+void CheckedCloseFile(FileHandle file);
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_FILE_FILE_IO_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/file/file_io_posix.cc b/chromium/third_party/crashpad/crashpad/util/file/file_io_posix.cc
new file mode 100644
index 00000000000..9511b34e721
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/file/file_io_posix.cc
@@ -0,0 +1,154 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/file/file_io.h"
+
+#include <fcntl.h>
+#include <sys/file.h>
+#include <unistd.h>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/posix/eintr_wrapper.h"
+
+namespace {
+
+struct ReadTraits {
+ using VoidBufferType = void*;
+ using CharBufferType = char*;
+ static ssize_t Operate(int fd, CharBufferType buffer, size_t size) {
+ return read(fd, buffer, size);
+ }
+};
+
+struct WriteTraits {
+ using VoidBufferType = const void*;
+ using CharBufferType = const char*;
+ static ssize_t Operate(int fd, CharBufferType buffer, size_t size) {
+ return write(fd, buffer, size);
+ }
+};
+
+template <typename Traits>
+ssize_t ReadOrWrite(int fd,
+ typename Traits::VoidBufferType buffer,
+ size_t size) {
+ typename Traits::CharBufferType buffer_c =
+ reinterpret_cast<typename Traits::CharBufferType>(buffer);
+
+ ssize_t total_bytes = 0;
+ while (size > 0) {
+ ssize_t bytes = HANDLE_EINTR(Traits::Operate(fd, buffer_c, size));
+ if (bytes < 0) {
+ return bytes;
+ } else if (bytes == 0) {
+ break;
+ }
+
+ buffer_c += bytes;
+ size -= bytes;
+ total_bytes += bytes;
+ }
+
+ return total_bytes;
+}
+
+} // namespace
+
+namespace crashpad {
+
+namespace {
+
+FileHandle LoggingOpenFileForOutput(int rdwr_or_wronly,
+ const base::FilePath& path,
+ FileWriteMode mode,
+ FilePermissions permissions) {
+ int flags = rdwr_or_wronly | O_CREAT;
+ // kReuseOrCreate does not need any additional flags.
+ if (mode == FileWriteMode::kTruncateOrCreate)
+ flags |= O_TRUNC;
+ else if (mode == FileWriteMode::kCreateOrFail)
+ flags |= O_EXCL;
+
+ int fd = HANDLE_EINTR(
+ open(path.value().c_str(),
+ flags,
+ permissions == FilePermissions::kWorldReadable ? 0644 : 0600));
+ PLOG_IF(ERROR, fd < 0) << "open " << path.value();
+ return fd;
+}
+
+} // namespace
+
+ssize_t ReadFile(FileHandle file, void* buffer, size_t size) {
+ return ReadOrWrite<ReadTraits>(file, buffer, size);
+}
+
+ssize_t WriteFile(FileHandle file, const void* buffer, size_t size) {
+ return ReadOrWrite<WriteTraits>(file, buffer, size);
+}
+
+FileHandle LoggingOpenFileForRead(const base::FilePath& path) {
+ int fd = HANDLE_EINTR(open(path.value().c_str(), O_RDONLY));
+ PLOG_IF(ERROR, fd < 0) << "open " << path.value();
+ return fd;
+}
+
+FileHandle LoggingOpenFileForWrite(const base::FilePath& path,
+ FileWriteMode mode,
+ FilePermissions permissions) {
+ return LoggingOpenFileForOutput(O_WRONLY, path, mode, permissions);
+}
+
+FileHandle LoggingOpenFileForReadAndWrite(const base::FilePath& path,
+ FileWriteMode mode,
+ FilePermissions permissions) {
+ return LoggingOpenFileForOutput(O_RDWR, path, mode, permissions);
+}
+
+bool LoggingLockFile(FileHandle file, FileLocking locking) {
+ int operation = (locking == FileLocking::kShared) ? LOCK_SH : LOCK_EX;
+ int rv = HANDLE_EINTR(flock(file, operation));
+ PLOG_IF(ERROR, rv != 0) << "flock";
+ return rv == 0;
+}
+
+bool LoggingUnlockFile(FileHandle file) {
+ int rv = flock(file, LOCK_UN);
+ PLOG_IF(ERROR, rv != 0) << "flock";
+ return rv == 0;
+}
+
+FileOffset LoggingSeekFile(FileHandle file, FileOffset offset, int whence) {
+ off_t rv = lseek(file, offset, whence);
+ PLOG_IF(ERROR, rv < 0) << "lseek";
+ return rv;
+}
+
+bool LoggingTruncateFile(FileHandle file) {
+ if (HANDLE_EINTR(ftruncate(file, 0)) != 0) {
+ PLOG(ERROR) << "ftruncate";
+ return false;
+ }
+ return true;
+}
+
+bool LoggingCloseFile(FileHandle file) {
+ int rv = IGNORE_EINTR(close(file));
+ PLOG_IF(ERROR, rv != 0) << "close";
+ return rv == 0;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/file/file_io_test.cc b/chromium/third_party/crashpad/crashpad/util/file/file_io_test.cc
new file mode 100644
index 00000000000..f91cde5ba3b
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/file/file_io_test.cc
@@ -0,0 +1,207 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/file/file_io.h"
+
+#include "base/atomicops.h"
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "gtest/gtest.h"
+#include "test/errors.h"
+#include "test/scoped_temp_dir.h"
+#include "test/thread.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+enum class ReadOrWrite : bool {
+ kRead,
+ kWrite,
+};
+
+void FileShareModeTest(ReadOrWrite first, ReadOrWrite second) {
+ ScopedTempDir temp_dir;
+ base::FilePath shared_file =
+ temp_dir.path().Append(FILE_PATH_LITERAL("shared_file"));
+ {
+ // Create an empty file to work on.
+ ScopedFileHandle create(
+ LoggingOpenFileForWrite(shared_file,
+ FileWriteMode::kCreateOrFail,
+ FilePermissions::kOwnerOnly));
+ }
+
+ auto handle1 = ScopedFileHandle(
+ (first == ReadOrWrite::kRead)
+ ? LoggingOpenFileForRead(shared_file)
+ : LoggingOpenFileForWrite(shared_file,
+ FileWriteMode::kReuseOrCreate,
+ FilePermissions::kOwnerOnly));
+ ASSERT_NE(handle1, kInvalidFileHandle);
+ auto handle2 = ScopedFileHandle(
+ (second == ReadOrWrite::kRead)
+ ? LoggingOpenFileForRead(shared_file)
+ : LoggingOpenFileForWrite(shared_file,
+ FileWriteMode::kReuseOrCreate,
+ FilePermissions::kOwnerOnly));
+ EXPECT_NE(handle2, kInvalidFileHandle);
+
+ EXPECT_NE(handle1.get(), handle2.get());
+}
+
+TEST(FileIO, FileShareMode_Read_Read) {
+ FileShareModeTest(ReadOrWrite::kRead, ReadOrWrite::kRead);
+}
+
+TEST(FileIO, FileShareMode_Read_Write) {
+ FileShareModeTest(ReadOrWrite::kRead, ReadOrWrite::kWrite);
+}
+
+TEST(FileIO, FileShareMode_Write_Read) {
+ FileShareModeTest(ReadOrWrite::kWrite, ReadOrWrite::kRead);
+}
+
+TEST(FileIO, FileShareMode_Write_Write) {
+ FileShareModeTest(ReadOrWrite::kWrite, ReadOrWrite::kWrite);
+}
+
+TEST(FileIO, MultipleSharedLocks) {
+ ScopedTempDir temp_dir;
+ base::FilePath shared_file =
+ temp_dir.path().Append(FILE_PATH_LITERAL("file_to_lock"));
+
+ {
+ // Create an empty file to lock.
+ ScopedFileHandle create(
+ LoggingOpenFileForWrite(shared_file,
+ FileWriteMode::kCreateOrFail,
+ FilePermissions::kOwnerOnly));
+ }
+
+ auto handle1 = ScopedFileHandle(LoggingOpenFileForRead(shared_file));
+ ASSERT_NE(handle1, kInvalidFileHandle);
+ EXPECT_TRUE(LoggingLockFile(handle1.get(), FileLocking::kShared));
+
+ auto handle2 = ScopedFileHandle(LoggingOpenFileForRead(shared_file));
+ ASSERT_NE(handle1, kInvalidFileHandle);
+ EXPECT_TRUE(LoggingLockFile(handle2.get(), FileLocking::kShared));
+
+ EXPECT_TRUE(LoggingUnlockFile(handle1.get()));
+ EXPECT_TRUE(LoggingUnlockFile(handle2.get()));
+}
+
+class LockingTestThread : public Thread {
+ public:
+ LockingTestThread()
+ : file_(), lock_type_(), iterations_(), actual_iterations_() {}
+
+ void Init(FileHandle file,
+ FileLocking lock_type,
+ int iterations,
+ base::subtle::Atomic32* actual_iterations) {
+ ASSERT_NE(file, kInvalidFileHandle);
+ file_ = ScopedFileHandle(file);
+ lock_type_ = lock_type;
+ iterations_ = iterations;
+ actual_iterations_ = actual_iterations;
+ }
+
+ private:
+ void ThreadMain() override {
+ for (int i = 0; i < iterations_; ++i) {
+ EXPECT_TRUE(LoggingLockFile(file_.get(), lock_type_));
+ base::subtle::NoBarrier_AtomicIncrement(actual_iterations_, 1);
+ EXPECT_TRUE(LoggingUnlockFile(file_.get()));
+ }
+ }
+
+ ScopedFileHandle file_;
+ FileLocking lock_type_;
+ int iterations_;
+ base::subtle::Atomic32* actual_iterations_;
+
+ DISALLOW_COPY_AND_ASSIGN(LockingTestThread);
+};
+
+void LockingTest(FileLocking main_lock, FileLocking other_locks) {
+ ScopedTempDir temp_dir;
+ base::FilePath shared_file =
+ temp_dir.path().Append(FILE_PATH_LITERAL("file_to_lock"));
+
+ {
+ // Create an empty file to lock.
+ ScopedFileHandle create(
+ LoggingOpenFileForWrite(shared_file,
+ FileWriteMode::kCreateOrFail,
+ FilePermissions::kOwnerOnly));
+ }
+
+ auto initial = ScopedFileHandle(
+ (main_lock == FileLocking::kShared)
+ ? LoggingOpenFileForRead(shared_file)
+ : LoggingOpenFileForWrite(shared_file,
+ FileWriteMode::kReuseOrCreate,
+ FilePermissions::kOwnerOnly));
+ ASSERT_NE(initial, kInvalidFileHandle);
+ ASSERT_TRUE(LoggingLockFile(initial.get(), main_lock));
+
+ base::subtle::Atomic32 actual_iterations = 0;
+
+ LockingTestThread threads[20];
+ int expected_iterations = 0;
+ for (size_t index = 0; index < arraysize(threads); ++index) {
+ int iterations_for_this_thread = static_cast<int>(index * 10);
+ threads[index].Init(
+ (other_locks == FileLocking::kShared)
+ ? LoggingOpenFileForRead(shared_file)
+ : LoggingOpenFileForWrite(shared_file,
+ FileWriteMode::kReuseOrCreate,
+ FilePermissions::kOwnerOnly),
+ other_locks,
+ iterations_for_this_thread,
+ &actual_iterations);
+ expected_iterations += iterations_for_this_thread;
+
+ ASSERT_NO_FATAL_FAILURE(threads[index].Start());
+ }
+
+ base::subtle::Atomic32 result =
+ base::subtle::NoBarrier_Load(&actual_iterations);
+ EXPECT_EQ(0, result);
+
+ ASSERT_TRUE(LoggingUnlockFile(initial.get()));
+
+ for (auto& t : threads)
+ t.Join();
+
+ result = base::subtle::NoBarrier_Load(&actual_iterations);
+ EXPECT_EQ(expected_iterations, result);
+}
+
+TEST(FileIO, ExclusiveVsExclusives) {
+ LockingTest(FileLocking::kExclusive, FileLocking::kExclusive);
+}
+
+TEST(FileIO, ExclusiveVsShareds) {
+ LockingTest(FileLocking::kExclusive, FileLocking::kShared);
+}
+
+TEST(FileIO, SharedVsExclusives) {
+ LockingTest(FileLocking::kShared, FileLocking::kExclusive);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/file/file_io_win.cc b/chromium/third_party/crashpad/crashpad/util/file/file_io_win.cc
new file mode 100644
index 00000000000..24a875011a8
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/file/file_io_win.cc
@@ -0,0 +1,212 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/file/file_io.h"
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+
+namespace {
+
+bool IsSocketHandle(HANDLE file) {
+ if (GetFileType(file) == FILE_TYPE_PIPE) {
+ // FILE_TYPE_PIPE means that it's a socket, a named pipe, or an anonymous
+ // pipe. If we are unable to retrieve the pipe information, we know it's a
+ // socket.
+ return !GetNamedPipeInfo(file, NULL, NULL, NULL, NULL);
+ }
+ return false;
+}
+
+} // namespace
+
+namespace crashpad {
+
+namespace {
+
+FileHandle LoggingOpenFileForOutput(DWORD access,
+ const base::FilePath& path,
+ FileWriteMode mode,
+ FilePermissions permissions) {
+ DWORD disposition = 0;
+ switch (mode) {
+ case FileWriteMode::kReuseOrCreate:
+ disposition = OPEN_ALWAYS;
+ break;
+ case FileWriteMode::kTruncateOrCreate:
+ disposition = CREATE_ALWAYS;
+ break;
+ case FileWriteMode::kCreateOrFail:
+ disposition = CREATE_NEW;
+ break;
+ }
+ HANDLE file = CreateFile(path.value().c_str(),
+ access,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr,
+ disposition,
+ FILE_ATTRIBUTE_NORMAL,
+ nullptr);
+ PLOG_IF(ERROR, file == INVALID_HANDLE_VALUE)
+ << "CreateFile " << base::UTF16ToUTF8(path.value());
+ return file;
+}
+
+} // namespace
+
+// TODO(scottmg): Handle > DWORD sized writes if necessary.
+
+ssize_t ReadFile(FileHandle file, void* buffer, size_t size) {
+ DCHECK(!IsSocketHandle(file));
+ DWORD size_dword = base::checked_cast<DWORD>(size);
+ DWORD total_read = 0;
+ char* buffer_c = reinterpret_cast<char*>(buffer);
+ while (size_dword > 0) {
+ DWORD bytes_read;
+ BOOL success = ::ReadFile(file, buffer_c, size_dword, &bytes_read, nullptr);
+ if (!success) {
+ if (GetLastError() == ERROR_BROKEN_PIPE) {
+ // When reading a pipe and the write handle has been closed, ReadFile
+ // fails with ERROR_BROKEN_PIPE, but only once all pending data has been
+ // read.
+ break;
+ } else if (GetLastError() != ERROR_MORE_DATA) {
+ return -1;
+ }
+ } else if (bytes_read == 0 && GetFileType(file) != FILE_TYPE_PIPE) {
+ // Zero bytes read for a file indicates reaching EOF. Zero bytes read from
+ // a pipe indicates only that there was a zero byte WriteFile issued on
+ // the other end, so continue reading.
+ break;
+ }
+
+ buffer_c += bytes_read;
+ size_dword -= bytes_read;
+ total_read += bytes_read;
+ }
+ return total_read;
+}
+
+ssize_t WriteFile(FileHandle file, const void* buffer, size_t size) {
+ // TODO(scottmg): This might need to handle the limit for pipes across a
+ // network in the future.
+ DWORD size_dword = base::checked_cast<DWORD>(size);
+ DWORD bytes_written;
+ BOOL rv = ::WriteFile(file, buffer, size_dword, &bytes_written, nullptr);
+ if (!rv)
+ return -1;
+ CHECK_EQ(bytes_written, size_dword);
+ return bytes_written;
+}
+
+FileHandle LoggingOpenFileForRead(const base::FilePath& path) {
+ HANDLE file = CreateFile(path.value().c_str(),
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr,
+ OPEN_EXISTING,
+ 0,
+ nullptr);
+ PLOG_IF(ERROR, file == INVALID_HANDLE_VALUE)
+ << "CreateFile " << base::UTF16ToUTF8(path.value());
+ return file;
+}
+
+FileHandle LoggingOpenFileForWrite(const base::FilePath& path,
+ FileWriteMode mode,
+ FilePermissions permissions) {
+ return LoggingOpenFileForOutput(GENERIC_WRITE, path, mode, permissions);
+}
+
+FileHandle LoggingOpenFileForReadAndWrite(const base::FilePath& path,
+ FileWriteMode mode,
+ FilePermissions permissions) {
+ return LoggingOpenFileForOutput(
+ GENERIC_READ | GENERIC_WRITE, path, mode, permissions);
+}
+
+bool LoggingLockFile(FileHandle file, FileLocking locking) {
+ DWORD flags =
+ (locking == FileLocking::kExclusive) ? LOCKFILE_EXCLUSIVE_LOCK : 0;
+
+ // Note that the `Offset` fields of overlapped indicate the start location for
+ // locking (beginning of file in this case), and `hEvent` must be also be set
+ // to 0.
+ OVERLAPPED overlapped = {0};
+ if (!LockFileEx(file, flags, 0, MAXDWORD, MAXDWORD, &overlapped)) {
+ PLOG(ERROR) << "LockFileEx";
+ return false;
+ }
+ return true;
+}
+
+bool LoggingUnlockFile(FileHandle file) {
+ // Note that the `Offset` fields of overlapped indicate the start location for
+ // locking (beginning of file in this case), and `hEvent` must be also be set
+ // to 0.
+ OVERLAPPED overlapped = {0};
+ if (!UnlockFileEx(file, 0, MAXDWORD, MAXDWORD, &overlapped)) {
+ PLOG(ERROR) << "UnlockFileEx";
+ return false;
+ }
+ return true;
+}
+
+FileOffset LoggingSeekFile(FileHandle file, FileOffset offset, int whence) {
+ DWORD method = 0;
+ switch (whence) {
+ case SEEK_SET:
+ method = FILE_BEGIN;
+ break;
+ case SEEK_CUR:
+ method = FILE_CURRENT;
+ break;
+ case SEEK_END:
+ method = FILE_END;
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ LARGE_INTEGER distance_to_move;
+ distance_to_move.QuadPart = offset;
+ LARGE_INTEGER new_offset;
+ BOOL result = SetFilePointerEx(file, distance_to_move, &new_offset, method);
+ if (!result) {
+ PLOG(ERROR) << "SetFilePointerEx";
+ return -1;
+ }
+ return new_offset.QuadPart;
+}
+
+bool LoggingTruncateFile(FileHandle file) {
+ if (LoggingSeekFile(file, 0, SEEK_SET) != 0)
+ return false;
+ if (!SetEndOfFile(file)) {
+ PLOG(ERROR) << "SetEndOfFile";
+ return false;
+ }
+ return true;
+}
+
+bool LoggingCloseFile(FileHandle file) {
+ BOOL rv = CloseHandle(file);
+ PLOG_IF(ERROR, !rv) << "CloseHandle";
+ return rv;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/file/file_reader.cc b/chromium/third_party/crashpad/crashpad/util/file/file_reader.cc
new file mode 100644
index 00000000000..edb3312632f
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/file/file_reader.cc
@@ -0,0 +1,100 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/file/file_reader.h"
+
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+
+namespace crashpad {
+
+bool FileReaderInterface::ReadExactly(void* data, size_t size) {
+ ssize_t expect = base::checked_cast<ssize_t>(size);
+ ssize_t rv = Read(data, size);
+ if (rv < 0) {
+ // Read() will have logged its own error.
+ return false;
+ } else if (rv != expect) {
+ LOG(ERROR) << "ReadExactly(): expected " << expect << ", observed " << rv;
+ return false;
+ }
+
+ return true;
+}
+
+WeakFileHandleFileReader::WeakFileHandleFileReader(FileHandle file_handle)
+ : file_handle_(file_handle) {
+}
+
+WeakFileHandleFileReader::~WeakFileHandleFileReader() {
+}
+
+ssize_t WeakFileHandleFileReader::Read(void* data, size_t size) {
+ DCHECK_NE(file_handle_, kInvalidFileHandle);
+
+ // Don’t use LoggingReadFile(), which insists on a full read and only returns
+ // a bool. This method permits short reads and returns the number of bytes
+ // read.
+ base::checked_cast<ssize_t>(size);
+ ssize_t rv = ReadFile(file_handle_, data, size);
+ if (rv < 0) {
+ PLOG(ERROR) << "read";
+ return -1;
+ }
+
+ return rv;
+}
+
+FileOffset WeakFileHandleFileReader::Seek(FileOffset offset, int whence) {
+ DCHECK_NE(file_handle_, kInvalidFileHandle);
+ return LoggingSeekFile(file_handle_, offset, whence);
+}
+
+FileReader::FileReader()
+ : file_(),
+ weak_file_handle_file_reader_(kInvalidFileHandle) {
+}
+
+FileReader::~FileReader() {
+}
+
+bool FileReader::Open(const base::FilePath& path) {
+ CHECK(!file_.is_valid());
+ file_.reset(LoggingOpenFileForRead(path));
+ if (!file_.is_valid()) {
+ return false;
+ }
+
+ weak_file_handle_file_reader_.set_file_handle(file_.get());
+ return true;
+}
+
+void FileReader::Close() {
+ CHECK(file_.is_valid());
+
+ weak_file_handle_file_reader_.set_file_handle(kInvalidFileHandle);
+ file_.reset();
+}
+
+ssize_t FileReader::Read(void* data, size_t size) {
+ DCHECK(file_.is_valid());
+ return weak_file_handle_file_reader_.Read(data, size);
+}
+
+FileOffset FileReader::Seek(FileOffset offset, int whence) {
+ DCHECK(file_.is_valid());
+ return weak_file_handle_file_reader_.Seek(offset, whence);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/file/file_reader.h b/chromium/third_party/crashpad/crashpad/util/file/file_reader.h
new file mode 100644
index 00000000000..0c6856f1a80
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/file/file_reader.h
@@ -0,0 +1,147 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_FILE_FILE_READER_H_
+#define CRASHPAD_UTIL_FILE_FILE_READER_H_
+
+#include <sys/types.h>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "util/file/file_io.h"
+#include "util/file/file_seeker.h"
+
+namespace crashpad {
+
+//! \brief An interface to read to files and other file-like objects with
+//! semantics matching the underlying platform (POSIX or Windows).
+class FileReaderInterface : public virtual FileSeekerInterface {
+ public:
+ //! \brief Wraps ReadFile(), or provides an implementation with identical
+ //! semantics.
+ //!
+ //! \return The number of bytes actually read if the operation succeeded,
+ //! which may be `0` or any positive value less than or equal to \a size.
+ //! `-1` if the operation failed, with an error message logged.
+ virtual ssize_t Read(void* data, size_t size) = 0;
+
+ //! \brief Wraps Read(), ensuring that the read succeeded and exactly \a size
+ //! bytes were read.
+ //!
+ //! Semantically, this behaves as LoggingReadFile().
+ //!
+ //! \return `true` if the operation succeeded, `false` if it failed, with an
+ //! error message logged. Short reads are treated as failures.
+ bool ReadExactly(void* data, size_t size);
+
+ protected:
+ ~FileReaderInterface() {}
+};
+
+//! \brief A file reader backed by a FileHandle.
+//!
+//! FileReader requires users to provide a FilePath to open, but this class
+//! accepts an already-open FileHandle instead. Like FileReader, this class may
+//! read from a filesystem-based file, but unlike FileReader, this class is not
+//! responsible for opening or closing the file. Users of this class must ensure
+//! that the file handle is closed appropriately elsewhere. Objects of this
+//! class may be used to read from file handles not associated with
+//! filesystem-based files, although special attention should be paid to the
+//! Seek() method, which may not function on file handles that do not refer to
+//! disk-based files.
+//!
+//! This class is expected to be used when other code is responsible for
+//! opening files and already provides file handles.
+class WeakFileHandleFileReader : public FileReaderInterface {
+ public:
+ explicit WeakFileHandleFileReader(FileHandle file_handle);
+ ~WeakFileHandleFileReader();
+
+ // FileReaderInterface:
+ ssize_t Read(void* data, size_t size) override;
+
+ // FileSeekerInterface:
+
+ //! \copydoc FileReaderInterface::Seek()
+ //!
+ //! \note This method is only guaranteed to function on file handles referring
+ //! to disk-based files.
+ FileOffset Seek(FileOffset offset, int whence) override;
+
+ private:
+ void set_file_handle(FileHandle file_handle) { file_handle_ = file_handle; }
+
+ FileHandle file_handle_; // weak
+
+ // FileReader uses this class as its internal implementation, and it needs to
+ // be able to call set_file_handle(). FileReader cannot initialize a
+ // WeakFileHandleFileReader with a correct file descriptor at the time of
+ // construction because no file descriptor will be available until
+ // FileReader::Open() is called.
+ friend class FileReader;
+
+ DISALLOW_COPY_AND_ASSIGN(WeakFileHandleFileReader);
+};
+
+//! \brief A file reader implementation that wraps traditional system file
+//! operations on files accessed through the filesystem.
+class FileReader : public FileReaderInterface {
+ public:
+ FileReader();
+ ~FileReader();
+
+ // FileReaderInterface:
+
+ //! \brief Wraps LoggingOpenFileForRead().
+ //!
+ //! \return `true` if the operation succeeded, `false` if it failed, with an
+ //! error message logged.
+ //!
+ //! \note After a successful call, this method cannot be called again until
+ //! after Close().
+ bool Open(const base::FilePath& path);
+
+ //! \brief Wraps CheckedCloseHandle().
+ //!
+ //! \note It is only valid to call this method on an object that has had a
+ //! successful Open() that has not yet been matched by a subsequent call
+ //! to this method.
+ void Close();
+
+ // FileReaderInterface:
+
+ //! \copydoc FileReaderInterface::Read()
+ //!
+ //! \note It is only valid to call this method between a successful Open() and
+ //! a Close().
+ ssize_t Read(void* data, size_t size) override;
+
+ // FileSeekerInterface:
+
+ //! \copydoc FileReaderInterface::Seek()
+ //!
+ //! \note It is only valid to call this method between a successful Open() and
+ //! a Close().
+ FileOffset Seek(FileOffset offset, int whence) override;
+
+ private:
+ ScopedFileHandle file_;
+ WeakFileHandleFileReader weak_file_handle_file_reader_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileReader);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_FILE_FILE_READER_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/file/file_seeker.cc b/chromium/third_party/crashpad/crashpad/util/file/file_seeker.cc
new file mode 100644
index 00000000000..a38e2f91854
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/file/file_seeker.cc
@@ -0,0 +1,37 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/file/file_seeker.h"
+
+#include "base/logging.h"
+
+namespace crashpad {
+
+FileOffset FileSeekerInterface::SeekGet() {
+ return Seek(0, SEEK_CUR);
+}
+
+bool FileSeekerInterface::SeekSet(FileOffset offset) {
+ FileOffset rv = Seek(offset, SEEK_SET);
+ if (rv < 0) {
+ // Seek() will have logged its own error.
+ return false;
+ } else if (rv != offset) {
+ LOG(ERROR) << "SeekSet(): expected " << offset << ", observed " << rv;
+ return false;
+ }
+ return true;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/file/file_seeker.h b/chromium/third_party/crashpad/crashpad/util/file/file_seeker.h
new file mode 100644
index 00000000000..1b32e104588
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/file/file_seeker.h
@@ -0,0 +1,54 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_FILE_FILE_SEEKER_H_
+#define CRASHPAD_UTIL_FILE_FILE_SEEKER_H_
+
+#include "util/file/file_io.h"
+
+namespace crashpad {
+
+//! \brief An interface to seek in files and other file-like objects with
+//! semantics matching the underlying platform (POSIX or Windows).
+class FileSeekerInterface {
+ public:
+ //! \brief Wraps LoggingFileSeek() or provides an alternate implementation
+ //! with identical semantics.
+ //!
+ //! \return The return value of LoggingFileSeek(). `-1` on failure,
+ //! with an error message logged.
+ virtual FileOffset Seek(FileOffset offset, int whence) = 0;
+
+ //! \brief Wraps Seek(), using `SEEK_CUR` to obtain the file’s current
+ //! position.
+ //!
+ //! \return The file’s current position on success. `-1` on failure, with an
+ //! error message logged.
+ FileOffset SeekGet();
+
+ //! \brief Wraps Seek(), using `SEEK_SET`, ensuring that the seek succeeded
+ //! and the file is positioned as desired.
+ //!
+ //! \return `true` if the operation succeeded, `false` if it failed, with an
+ //! error message logged. A failure to reposition the file as desired is
+ //! treated as a failure.
+ bool SeekSet(FileOffset offset);
+
+ protected:
+ ~FileSeekerInterface() {}
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_FILE_FILE_SEEKER_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/file/file_writer.cc b/chromium/third_party/crashpad/crashpad/util/file/file_writer.cc
new file mode 100644
index 00000000000..aedcd5dce3b
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/file/file_writer.cc
@@ -0,0 +1,178 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/file/file_writer.h"
+
+#include <algorithm>
+
+#include <limits.h>
+
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "build/build_config.h"
+
+#if defined(OS_POSIX)
+#include <sys/uio.h>
+#include <unistd.h>
+#include "base/posix/eintr_wrapper.h"
+#endif // OS_POSIX
+
+namespace crashpad {
+
+#if defined(OS_POSIX)
+// Ensure type compatibility between WritableIoVec and iovec.
+static_assert(sizeof(WritableIoVec) == sizeof(iovec), "WritableIoVec size");
+static_assert(offsetof(WritableIoVec, iov_base) == offsetof(iovec, iov_base),
+ "WritableIoVec base offset");
+static_assert(offsetof(WritableIoVec, iov_len) == offsetof(iovec, iov_len),
+ "WritableIoVec len offset");
+#endif // OS_POSIX
+
+WeakFileHandleFileWriter::WeakFileHandleFileWriter(FileHandle file_handle)
+ : file_handle_(file_handle) {
+}
+
+WeakFileHandleFileWriter::~WeakFileHandleFileWriter() {
+}
+
+bool WeakFileHandleFileWriter::Write(const void* data, size_t size) {
+ DCHECK_NE(file_handle_, kInvalidFileHandle);
+ return LoggingWriteFile(file_handle_, data, size);
+}
+
+bool WeakFileHandleFileWriter::WriteIoVec(std::vector<WritableIoVec>* iovecs) {
+ DCHECK_NE(file_handle_, kInvalidFileHandle);
+
+#if defined(OS_POSIX)
+
+ ssize_t size = 0;
+ for (const WritableIoVec& iov : *iovecs) {
+ // TODO(mark): Check to avoid overflow of ssize_t, and fail with EINVAL.
+ size += iov.iov_len;
+ }
+
+ // Get an iovec*, because that’s what writev wants. The only difference
+ // between WritableIoVec and iovec is that WritableIoVec’s iov_base is a
+ // pointer to a const buffer, where iovec’s iov_base isn’t. writev doesn’t
+ // actually write to the data, so this cast is safe here. iovec’s iov_base is
+ // non-const because the same structure is used for readv and writev, and
+ // readv needs to write to the buffer that iov_base points to.
+ iovec* iov = reinterpret_cast<iovec*>(&(*iovecs)[0]);
+ size_t remaining_iovecs = iovecs->size();
+
+ while (size > 0) {
+ size_t writev_iovec_count =
+ std::min(remaining_iovecs, implicit_cast<size_t>(IOV_MAX));
+ ssize_t written =
+ HANDLE_EINTR(writev(file_handle_, iov, writev_iovec_count));
+ if (written < 0) {
+ PLOG(ERROR) << "writev";
+ return false;
+ } else if (written == 0) {
+ LOG(ERROR) << "writev: returned 0";
+ return false;
+ }
+
+ size -= written;
+ DCHECK_GE(size, 0);
+
+ if (size == 0) {
+ remaining_iovecs = 0;
+ break;
+ }
+
+ while (written > 0) {
+ size_t wrote_this_iovec =
+ std::min(implicit_cast<size_t>(written), iov->iov_len);
+ written -= wrote_this_iovec;
+ if (wrote_this_iovec < iov->iov_len) {
+ iov->iov_base =
+ reinterpret_cast<char*>(iov->iov_base) + wrote_this_iovec;
+ iov->iov_len -= wrote_this_iovec;
+ } else {
+ ++iov;
+ --remaining_iovecs;
+ }
+ }
+ }
+
+ DCHECK_EQ(remaining_iovecs, 0u);
+
+#else // !OS_POSIX
+
+ for (const WritableIoVec& iov : *iovecs) {
+ if (!Write(iov.iov_base, iov.iov_len))
+ return false;
+ }
+
+#endif // OS_POSIX
+
+#ifndef NDEBUG
+ // The interface says that |iovecs| is not sacred, so scramble it to make sure
+ // that nobody depends on it.
+ memset(&(*iovecs)[0], 0xa5, sizeof((*iovecs)[0]) * iovecs->size());
+#endif
+
+ return true;
+}
+
+FileOffset WeakFileHandleFileWriter::Seek(FileOffset offset, int whence) {
+ DCHECK_NE(file_handle_, kInvalidFileHandle);
+ return LoggingSeekFile(file_handle_, offset, whence);
+}
+
+FileWriter::FileWriter()
+ : file_(),
+ weak_file_handle_file_writer_(kInvalidFileHandle) {
+}
+
+FileWriter::~FileWriter() {
+}
+
+bool FileWriter::Open(const base::FilePath& path,
+ FileWriteMode write_mode,
+ FilePermissions permissions) {
+ CHECK(!file_.is_valid());
+ file_.reset(LoggingOpenFileForWrite(path, write_mode, permissions));
+ if (!file_.is_valid()) {
+ return false;
+ }
+
+ weak_file_handle_file_writer_.set_file_handle(file_.get());
+ return true;
+}
+
+void FileWriter::Close() {
+ CHECK(file_.is_valid());
+
+ weak_file_handle_file_writer_.set_file_handle(kInvalidFileHandle);
+ file_.reset();
+}
+
+bool FileWriter::Write(const void* data, size_t size) {
+ DCHECK(file_.is_valid());
+ return weak_file_handle_file_writer_.Write(data, size);
+}
+
+bool FileWriter::WriteIoVec(std::vector<WritableIoVec>* iovecs) {
+ DCHECK(file_.is_valid());
+ return weak_file_handle_file_writer_.WriteIoVec(iovecs);
+}
+
+FileOffset FileWriter::Seek(FileOffset offset, int whence) {
+ DCHECK(file_.is_valid());
+ return weak_file_handle_file_writer_.Seek(offset, whence);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/file/file_writer.h b/chromium/third_party/crashpad/crashpad/util/file/file_writer.h
new file mode 100644
index 00000000000..fd88ce08c54
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/file/file_writer.h
@@ -0,0 +1,173 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_FILE_FILE_WRITER_H_
+#define CRASHPAD_UTIL_FILE_FILE_WRITER_H_
+
+#include <sys/types.h>
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "util/file/file_io.h"
+#include "util/file/file_seeker.h"
+
+namespace crashpad {
+
+//! \brief A version of `iovec` with a `const` #iov_base field.
+//!
+//! This structure is intended to be used for write operations.
+//
+// Type compatibility with iovec is tested with static assertions in the
+// implementation file.
+struct WritableIoVec {
+ //! \brief The base address of a memory region for output.
+ const void* iov_base;
+
+ //! \brief The size of the memory pointed to by #iov_base.
+ size_t iov_len;
+};
+
+//! \brief An interface to write to files and other file-like objects with
+//! semantics matching the underlying platform (POSIX or Windows).
+class FileWriterInterface : public virtual FileSeekerInterface {
+ public:
+ //! \brief Wraps LoggingWriteFile(), or provides an implementation with
+ //! identical semantics.
+ //!
+ //! \return `true` if the operation succeeded, `false` if it failed, with an
+ //! error message logged.
+ virtual bool Write(const void* data, size_t size) = 0;
+
+ //! \brief Wraps `writev()` on POSIX or provides an alternate implementation
+ //! with identical semantics. This method will write entire buffers,
+ //! continuing after a short write or after being interrupted. On
+ //! non-POSIX this is a simple wrapper around Write().
+ //!
+ //! \return `true` if the operation succeeded, `false` if it failed, with an
+ //! error message logged.
+ //!
+ //! \note The contents of \a iovecs are undefined when this method returns.
+ virtual bool WriteIoVec(std::vector<WritableIoVec>* iovecs) = 0;
+
+ protected:
+ ~FileWriterInterface() {}
+};
+
+//! \brief A file writer backed by a FileHandle.
+//!
+//! FileWriter requires users to provide a FilePath to open, but this class
+//! accepts an already-open FileHandle instead. Like FileWriter, this class may
+//! write to a filesystem-based file, but unlike FileWriter, this class is not
+//! responsible for creating or closing the file. Users of this class must
+//! ensure that the file handle is closed appropriately elsewhere. Objects of
+//! this class may be used to write to file handles not associated with
+//! filesystem-based files, although special attention should be paid to the
+//! Seek() method, which may not function on file handles that do not refer to
+//! disk-based files.
+//!
+//! This class is expected to be used when other code is responsible for
+//! creating files and already provides file handles.
+class WeakFileHandleFileWriter : public FileWriterInterface {
+ public:
+ explicit WeakFileHandleFileWriter(FileHandle file_handle);
+ ~WeakFileHandleFileWriter();
+
+ // FileWriterInterface:
+ bool Write(const void* data, size_t size) override;
+ bool WriteIoVec(std::vector<WritableIoVec>* iovecs) override;
+
+ // FileSeekerInterface:
+
+ //! \copydoc FileWriterInterface::Seek()
+ //!
+ //! \note This method is only guaranteed to function on file handles referring
+ //! to disk-based files.
+ FileOffset Seek(FileOffset offset, int whence) override;
+
+ private:
+ void set_file_handle(FileHandle file_handle) { file_handle_ = file_handle; }
+
+ FileHandle file_handle_; // weak
+
+ // FileWriter uses this class as its internal implementation, and it needs to
+ // be able to call set_file_handle(). FileWriter cannot initialize a
+ // WeakFileHandleFileWriter with a correct file descriptor at the time of
+ // construction because no file descriptor will be available until
+ // FileWriter::Open() is called.
+ friend class FileWriter;
+
+ DISALLOW_COPY_AND_ASSIGN(WeakFileHandleFileWriter);
+};
+
+//! \brief A file writer implementation that wraps traditional system file
+//! operations on files accessed through the filesystem.
+class FileWriter : public FileWriterInterface {
+ public:
+ FileWriter();
+ ~FileWriter();
+
+ // FileWriterInterface:
+
+ //! \brief Wraps LoggingOpenFileForWrite().
+ //!
+ //! \return `true` if the operation succeeded, `false` if it failed, with an
+ //! error message logged.
+ //!
+ //! \note After a successful call, this method cannot be called again until
+ //! after Close().
+ bool Open(const base::FilePath& path,
+ FileWriteMode write_mode,
+ FilePermissions permissions);
+
+ //! \brief Wraps CheckedCloseHandle().
+ //!
+ //! \note It is only valid to call this method on an object that has had a
+ //! successful Open() that has not yet been matched by a subsequent call
+ //! to this method.
+ void Close();
+
+ // FileWriterInterface:
+
+ //! \copydoc FileWriterInterface::Write()
+ //!
+ //! \note It is only valid to call this method between a successful Open() and
+ //! a Close().
+ bool Write(const void* data, size_t size) override;
+
+ //! \copydoc FileWriterInterface::WriteIoVec()
+ //!
+ //! \note It is only valid to call this method between a successful Open() and
+ //! a Close().
+ bool WriteIoVec(std::vector<WritableIoVec>* iovecs) override;
+
+ // FileSeekerInterface:
+
+ //! \copydoc FileWriterInterface::Seek()
+ //!
+ //! \note It is only valid to call this method between a successful Open() and
+ //! a Close().
+ FileOffset Seek(FileOffset offset, int whence) override;
+
+ private:
+ ScopedFileHandle file_;
+ WeakFileHandleFileWriter weak_file_handle_file_writer_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileWriter);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_FILE_FILE_WRITER_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/file/string_file.cc b/chromium/third_party/crashpad/crashpad/util/file/string_file.cc
new file mode 100644
index 00000000000..755ba2d9c20
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/file/string_file.cc
@@ -0,0 +1,171 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/file/string_file.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <limits>
+
+#include "base/logging.h"
+#include "base/numerics/safe_math.h"
+#include "util/numeric/safe_assignment.h"
+
+namespace crashpad {
+
+StringFile::StringFile() : string_(), offset_(0) {
+}
+
+StringFile::~StringFile() {
+}
+
+void StringFile::SetString(const std::string& string) {
+ CHECK_LE(string.size(),
+ implicit_cast<size_t>(std::numeric_limits<ssize_t>::max()));
+ string_ = string;
+ offset_ = 0;
+}
+
+void StringFile::Reset() {
+ string_.clear();
+ offset_ = 0;
+}
+
+ssize_t StringFile::Read(void* data, size_t size) {
+ DCHECK(offset_.IsValid());
+
+ const size_t offset = offset_.ValueOrDie();
+ if (offset >= string_.size()) {
+ return 0;
+ }
+
+ const size_t nread = std::min(size, string_.size() - offset);
+
+ base::CheckedNumeric<ssize_t> new_offset = offset_;
+ new_offset += nread;
+ if (!new_offset.IsValid()) {
+ LOG(ERROR) << "Read(): file too large";
+ return -1;
+ }
+
+ memcpy(data, &string_[offset], nread);
+ offset_ = new_offset;
+
+ return nread;
+}
+
+bool StringFile::Write(const void* data, size_t size) {
+ DCHECK(offset_.IsValid());
+
+ const size_t offset = offset_.ValueOrDie();
+ if (offset > string_.size()) {
+ string_.resize(offset);
+ }
+
+ base::CheckedNumeric<ssize_t> new_offset = offset_;
+ new_offset += size;
+ if (!new_offset.IsValid()) {
+ LOG(ERROR) << "Write(): file too large";
+ return false;
+ }
+
+ string_.replace(offset, size, reinterpret_cast<const char*>(data), size);
+ offset_ = new_offset;
+
+ return true;
+}
+
+bool StringFile::WriteIoVec(std::vector<WritableIoVec>* iovecs) {
+ DCHECK(offset_.IsValid());
+
+ if (iovecs->empty()) {
+ LOG(ERROR) << "WriteIoVec(): no iovecs";
+ return false;
+ }
+
+ // Avoid writing anything at all if it would cause an overflow.
+ base::CheckedNumeric<ssize_t> new_offset = offset_;
+ for (const WritableIoVec& iov : *iovecs) {
+ new_offset += iov.iov_len;
+ if (!new_offset.IsValid()) {
+ LOG(ERROR) << "WriteIoVec(): file too large";
+ return false;
+ }
+ }
+
+ for (const WritableIoVec& iov : *iovecs) {
+ if (!Write(iov.iov_base, iov.iov_len)) {
+ return false;
+ }
+ }
+
+#ifndef NDEBUG
+ // The interface says that |iovecs| is not sacred, so scramble it to make sure
+ // that nobody depends on it.
+ memset(&(*iovecs)[0], 0xa5, sizeof((*iovecs)[0]) * iovecs->size());
+#endif
+
+ return true;
+}
+
+FileOffset StringFile::Seek(FileOffset offset, int whence) {
+ DCHECK(offset_.IsValid());
+
+ size_t base_offset;
+
+ switch (whence) {
+ case SEEK_SET:
+ base_offset = 0;
+ break;
+
+ case SEEK_CUR:
+ base_offset = offset_.ValueOrDie();
+ break;
+
+ case SEEK_END:
+ base_offset = string_.size();
+ break;
+
+ default:
+ LOG(ERROR) << "Seek(): invalid whence " << whence;
+ return -1;
+ }
+
+ FileOffset base_offset_fileoffset;
+ if (!AssignIfInRange(&base_offset_fileoffset, base_offset)) {
+ LOG(ERROR) << "Seek(): base_offset " << base_offset
+ << " invalid for FileOffset";
+ return -1;
+ }
+ base::CheckedNumeric<FileOffset> new_offset(base_offset_fileoffset);
+ new_offset += offset;
+ if (!new_offset.IsValid()) {
+ LOG(ERROR) << "Seek(): new_offset invalid";
+ return -1;
+ }
+ FileOffset new_offset_fileoffset = new_offset.ValueOrDie();
+ size_t new_offset_sizet;
+ if (!AssignIfInRange(&new_offset_sizet, new_offset_fileoffset)) {
+ LOG(ERROR) << "Seek(): new_offset " << new_offset_fileoffset
+ << " invalid for size_t";
+ return -1;
+ }
+
+ offset_ = new_offset_sizet;
+
+ return offset_.ValueOrDie();
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/file/string_file.h b/chromium/third_party/crashpad/crashpad/util/file/string_file.h
new file mode 100644
index 00000000000..b6876b328f2
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/file/string_file.h
@@ -0,0 +1,80 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_FILE_STRING_FILE_H_
+#define CRASHPAD_UTIL_FILE_STRING_FILE_H_
+
+#include <sys/types.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/numerics/safe_math.h"
+#include "util/file/file_reader.h"
+#include "util/file/file_writer.h"
+
+namespace crashpad {
+
+//! \brief A file reader and writer backed by a virtual file, as opposed to a
+//! file on disk or other operating system file descriptor-based file.
+//!
+//! The virtual file is a buffer in memory. This class is convenient for use
+//! with other code that normally expects to read or write files, when it is
+//! impractical or inconvenient to read or write a file. It is expected that
+//! tests, in particular, will benefit from using this class.
+class StringFile : public FileReaderInterface, public FileWriterInterface {
+ public:
+ StringFile();
+ ~StringFile();
+
+ //! \brief Returns a string containing the virtual file’s contents.
+ const std::string& string() const { return string_; }
+
+ //! \brief Sets the virtual file’s contents to \a string, and resets its file
+ //! position to `0`.
+ void SetString(const std::string& string);
+
+ //! \brief Resets the virtual file’s contents to be empty, and resets its file
+ //! position to `0`.
+ void Reset();
+
+ // FileReaderInterface:
+ ssize_t Read(void* data, size_t size) override;
+
+ // FileWriterInterface:
+ bool Write(const void* data, size_t size) override;
+ bool WriteIoVec(std::vector<WritableIoVec>* iovecs) override;
+
+ // FileSeekerInterface:
+ FileOffset Seek(FileOffset offset, int whence) override;
+
+ private:
+ //! \brief The virtual file’s contents.
+ std::string string_;
+
+ //! \brief The file offset of the virtual file.
+ //!
+ //! \note This is stored in a `size_t` to match the characteristics of
+ //! #string_, the `std::string` used to store the virtual file’s contents.
+ //! This type will have different characteristics than the `off_t` used to
+ //! report file offsets. The implementation must take care when converting
+ //! between these distinct types.
+ base::CheckedNumeric<size_t> offset_;
+
+ DISALLOW_COPY_AND_ASSIGN(StringFile);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_FILE_STRING_FILE_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/file/string_file_test.cc b/chromium/third_party/crashpad/crashpad/util/file/string_file_test.cc
new file mode 100644
index 00000000000..c585610b225
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/file/string_file_test.cc
@@ -0,0 +1,497 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/file/string_file.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <limits>
+
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(StringFile, EmptyFile) {
+ StringFile string_file;
+ EXPECT_TRUE(string_file.string().empty());
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR));
+ EXPECT_TRUE(string_file.Write("", 0));
+ EXPECT_TRUE(string_file.string().empty());
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR));
+
+ char c = '6';
+ EXPECT_EQ(0, string_file.Read(&c, 1));
+ EXPECT_EQ('6', c);
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_TRUE(string_file.string().empty());
+}
+
+TEST(StringFile, OneByteFile) {
+ StringFile string_file;
+
+ EXPECT_TRUE(string_file.Write("a", 1));
+ EXPECT_EQ(1u, string_file.string().size());
+ EXPECT_EQ("a", string_file.string());
+ EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR));
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_SET));
+ char c = '6';
+ EXPECT_EQ(1, string_file.Read(&c, 1));
+ EXPECT_EQ('a', c);
+ EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR));
+ EXPECT_EQ(0, string_file.Read(&c, 1));
+ EXPECT_EQ('a', c);
+ EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR));
+ EXPECT_EQ("a", string_file.string());
+
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_SET));
+ EXPECT_TRUE(string_file.Write("b", 1));
+ EXPECT_EQ(1u, string_file.string().size());
+ EXPECT_EQ("b", string_file.string());
+ EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR));
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_SET));
+ EXPECT_EQ(1, string_file.Read(&c, 1));
+ EXPECT_EQ('b', c);
+ EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR));
+ EXPECT_EQ(0, string_file.Read(&c, 1));
+ EXPECT_EQ('b', c);
+ EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR));
+ EXPECT_EQ("b", string_file.string());
+
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_SET));
+ EXPECT_TRUE(string_file.Write("\0", 1));
+ EXPECT_EQ(1u, string_file.string().size());
+ EXPECT_EQ('\0', string_file.string()[0]);
+ EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR));
+ EXPECT_EQ(1u, string_file.string().size());
+ EXPECT_EQ('\0', string_file.string()[0]);
+}
+
+TEST(StringFile, SetString) {
+ char kString1[] = "Four score";
+ StringFile string_file;
+ string_file.SetString(kString1);
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_SET));
+ char buf[5] = "****";
+ EXPECT_EQ(4, string_file.Read(buf, 4));
+ EXPECT_STREQ("Four", buf);
+ EXPECT_EQ(4, string_file.Seek(0, SEEK_CUR));
+ EXPECT_EQ(static_cast<FileOffset>(strlen(kString1)),
+ string_file.Seek(0, SEEK_END));
+ EXPECT_EQ(kString1, string_file.string());
+
+ char kString2[] = "and seven years ago";
+ EXPECT_EQ(4, string_file.Seek(4, SEEK_SET));
+ EXPECT_EQ(4, string_file.Seek(0, SEEK_CUR));
+ string_file.SetString(kString2);
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR));
+ EXPECT_EQ(4, string_file.Read(buf, 4));
+ EXPECT_STREQ("and ", buf);
+ EXPECT_EQ(static_cast<FileOffset>(strlen(kString2)),
+ string_file.Seek(0, SEEK_END));
+ EXPECT_EQ(kString2, string_file.string());
+
+ char kString3[] = "our fathers";
+ EXPECT_EQ(4, string_file.Seek(4, SEEK_SET));
+ EXPECT_EQ(4, string_file.Seek(0, SEEK_CUR));
+ string_file.SetString(kString3);
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR));
+ EXPECT_EQ(4, string_file.Read(buf, 4));
+ EXPECT_STREQ("our ", buf);
+ EXPECT_EQ(static_cast<FileOffset>(strlen(kString3)),
+ string_file.Seek(0, SEEK_END));
+ EXPECT_EQ(kString3, string_file.string());
+}
+
+TEST(StringFile, ReadExactly) {
+ StringFile string_file;
+ string_file.SetString("1234567");
+ char buf[4] = "***";
+ EXPECT_TRUE(string_file.ReadExactly(buf, 3));
+ EXPECT_STREQ("123", buf);
+ EXPECT_TRUE(string_file.ReadExactly(buf, 3));
+ EXPECT_STREQ("456", buf);
+ EXPECT_FALSE(string_file.ReadExactly(buf, 3));
+}
+
+TEST(StringFile, Reset) {
+ StringFile string_file;
+
+ EXPECT_TRUE(string_file.Write("abc", 3));
+ EXPECT_EQ(3u, string_file.string().size());
+ EXPECT_EQ("abc", string_file.string());
+ EXPECT_EQ(3, string_file.Seek(0, SEEK_CUR));
+ char buf[10] = "*********";
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_SET));
+ EXPECT_EQ(3, string_file.Read(&buf, 10));
+ EXPECT_STREQ("abc******", buf);
+ EXPECT_EQ(3, string_file.Seek(0, SEEK_CUR));
+ EXPECT_FALSE(string_file.string().empty());
+
+ string_file.Reset();
+ EXPECT_TRUE(string_file.string().empty());
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_TRUE(string_file.Write("de", 2));
+ EXPECT_EQ(2u, string_file.string().size());
+ EXPECT_EQ("de", string_file.string());
+ EXPECT_EQ(2, string_file.Seek(0, SEEK_CUR));
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_SET));
+ EXPECT_EQ(2, string_file.Read(&buf, 10));
+ EXPECT_STREQ("dec******", buf);
+ EXPECT_EQ(2, string_file.Seek(0, SEEK_CUR));
+ EXPECT_FALSE(string_file.string().empty());
+
+ string_file.Reset();
+ EXPECT_TRUE(string_file.string().empty());
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_TRUE(string_file.Write("fghi", 4));
+ EXPECT_EQ(4u, string_file.string().size());
+ EXPECT_EQ("fghi", string_file.string());
+ EXPECT_EQ(4, string_file.Seek(0, SEEK_CUR));
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_SET));
+ EXPECT_EQ(2, string_file.Read(&buf, 2));
+ EXPECT_STREQ("fgc******", buf);
+ EXPECT_EQ(2, string_file.Seek(0, SEEK_CUR));
+ EXPECT_EQ(2, string_file.Read(&buf, 2));
+ EXPECT_STREQ("hic******", buf);
+ EXPECT_EQ(4, string_file.Seek(0, SEEK_CUR));
+ EXPECT_FALSE(string_file.string().empty());
+
+ string_file.Reset();
+ EXPECT_TRUE(string_file.string().empty());
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR));
+
+ // Test resetting after a sparse write.
+ EXPECT_EQ(1, string_file.Seek(1, SEEK_SET));
+ EXPECT_TRUE(string_file.Write("j", 1));
+ EXPECT_EQ(2u, string_file.string().size());
+ EXPECT_EQ(std::string("\0j", 2), string_file.string());
+ EXPECT_EQ(2, string_file.Seek(0, SEEK_CUR));
+ EXPECT_FALSE(string_file.string().empty());
+
+ string_file.Reset();
+ EXPECT_TRUE(string_file.string().empty());
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR));
+}
+
+TEST(StringFile, WriteInvalid) {
+ StringFile string_file;
+
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_FALSE(string_file.Write(
+ "", implicit_cast<size_t>(std::numeric_limits<ssize_t>::max()) + 1));
+ EXPECT_TRUE(string_file.string().empty());
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_TRUE(string_file.Write("a", 1));
+ EXPECT_FALSE(string_file.Write(
+ "", implicit_cast<size_t>(std::numeric_limits<ssize_t>::max())));
+ EXPECT_EQ(1u, string_file.string().size());
+ EXPECT_EQ("a", string_file.string());
+ EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR));
+}
+
+TEST(StringFile, WriteIoVec) {
+ StringFile string_file;
+
+ std::vector<WritableIoVec> iovecs;
+ WritableIoVec iov;
+ iov.iov_base = "";
+ iov.iov_len = 0;
+ iovecs.push_back(iov);
+ EXPECT_TRUE(string_file.WriteIoVec(&iovecs));
+ EXPECT_TRUE(string_file.string().empty());
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR));
+
+ iovecs.clear();
+ iov.iov_base = "a";
+ iov.iov_len = 1;
+ iovecs.push_back(iov);
+ EXPECT_TRUE(string_file.WriteIoVec(&iovecs));
+ EXPECT_EQ(1u, string_file.string().size());
+ EXPECT_EQ("a", string_file.string());
+ EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR));
+
+ iovecs.clear();
+ iovecs.push_back(iov);
+ EXPECT_TRUE(string_file.WriteIoVec(&iovecs));
+ EXPECT_EQ(2u, string_file.string().size());
+ EXPECT_EQ("aa", string_file.string());
+ EXPECT_EQ(2, string_file.Seek(0, SEEK_CUR));
+
+ iovecs.clear();
+ iovecs.push_back(iov);
+ iov.iov_base = "bc";
+ iov.iov_len = 2;
+ iovecs.push_back(iov);
+ EXPECT_TRUE(string_file.WriteIoVec(&iovecs));
+ EXPECT_EQ(5u, string_file.string().size());
+ EXPECT_EQ("aaabc", string_file.string());
+ EXPECT_EQ(5, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_TRUE(string_file.Write("def", 3));
+ EXPECT_EQ(8u, string_file.string().size());
+ EXPECT_EQ("aaabcdef", string_file.string());
+ EXPECT_EQ(8, string_file.Seek(0, SEEK_CUR));
+
+ iovecs.clear();
+ iov.iov_base = "ghij";
+ iov.iov_len = 4;
+ iovecs.push_back(iov);
+ iov.iov_base = "klmno";
+ iov.iov_len = 5;
+ iovecs.push_back(iov);
+ EXPECT_TRUE(string_file.WriteIoVec(&iovecs));
+ EXPECT_EQ(17u, string_file.string().size());
+ EXPECT_EQ("aaabcdefghijklmno", string_file.string());
+ EXPECT_EQ(17, string_file.Seek(0, SEEK_CUR));
+
+ string_file.Reset();
+ EXPECT_TRUE(string_file.string().empty());
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR));
+
+ iovecs.clear();
+ iov.iov_base = "abcd";
+ iov.iov_len = 4;
+ iovecs.resize(16, iov);
+ EXPECT_TRUE(string_file.WriteIoVec(&iovecs));
+ EXPECT_EQ(64u, string_file.string().size());
+ EXPECT_EQ("abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd",
+ string_file.string());
+ EXPECT_EQ(64, string_file.Seek(0, SEEK_CUR));
+}
+
+TEST(StringFile, WriteIoVecInvalid) {
+ StringFile string_file;
+
+ std::vector<WritableIoVec> iovecs;
+ EXPECT_FALSE(string_file.WriteIoVec(&iovecs));
+ EXPECT_TRUE(string_file.string().empty());
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR));
+
+ WritableIoVec iov;
+ EXPECT_EQ(1, string_file.Seek(1, SEEK_CUR));
+ iov.iov_base = "a";
+ iov.iov_len = std::numeric_limits<ssize_t>::max();
+ iovecs.push_back(iov);
+ EXPECT_FALSE(string_file.WriteIoVec(&iovecs));
+ EXPECT_TRUE(string_file.string().empty());
+ EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR));
+
+ iovecs.clear();
+ iov.iov_base = "a";
+ iov.iov_len = 1;
+ iovecs.push_back(iov);
+ iov.iov_len = std::numeric_limits<ssize_t>::max() - 1;
+ iovecs.push_back(iov);
+ EXPECT_FALSE(string_file.WriteIoVec(&iovecs));
+ EXPECT_TRUE(string_file.string().empty());
+ EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR));
+}
+
+TEST(StringFile, Seek) {
+ StringFile string_file;
+
+ EXPECT_TRUE(string_file.Write("abcd", 4));
+ EXPECT_EQ(4u, string_file.string().size());
+ EXPECT_EQ("abcd", string_file.string());
+ EXPECT_EQ(4, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_SET));
+ EXPECT_TRUE(string_file.Write("efgh", 4));
+ EXPECT_EQ(4u, string_file.string().size());
+ EXPECT_EQ("efgh", string_file.string());
+ EXPECT_EQ(4, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_SET));
+ EXPECT_TRUE(string_file.Write("ijk", 3));
+ EXPECT_EQ(4u, string_file.string().size());
+ EXPECT_EQ("ijkh", string_file.string());
+ EXPECT_EQ(3, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_SET));
+ EXPECT_TRUE(string_file.Write("lmnop", 5));
+ EXPECT_EQ(5u, string_file.string().size());
+ EXPECT_EQ("lmnop", string_file.string());
+ EXPECT_EQ(5, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_EQ(1, string_file.Seek(1, SEEK_SET));
+ EXPECT_TRUE(string_file.Write("q", 1));
+ EXPECT_EQ(5u, string_file.string().size());
+ EXPECT_EQ("lqnop", string_file.string());
+ EXPECT_EQ(2, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_EQ(1, string_file.Seek(-1, SEEK_CUR));
+ EXPECT_TRUE(string_file.Write("r", 1));
+ EXPECT_EQ(5u, string_file.string().size());
+ EXPECT_EQ("lrnop", string_file.string());
+ EXPECT_EQ(2, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_TRUE(string_file.Write("s", 1));
+ EXPECT_EQ(5u, string_file.string().size());
+ EXPECT_EQ("lrsop", string_file.string());
+ EXPECT_EQ(3, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_EQ(2, string_file.Seek(-1, SEEK_CUR));
+ EXPECT_TRUE(string_file.Write("t", 1));
+ EXPECT_EQ(5u, string_file.string().size());
+ EXPECT_EQ("lrtop", string_file.string());
+ EXPECT_EQ(3, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_EQ(4, string_file.Seek(-1, SEEK_END));
+ EXPECT_TRUE(string_file.Write("u", 1));
+ EXPECT_EQ(5u, string_file.string().size());
+ EXPECT_EQ("lrtou", string_file.string());
+ EXPECT_EQ(5, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_EQ(0, string_file.Seek(-5, SEEK_END));
+ EXPECT_TRUE(string_file.Write("v", 1));
+ EXPECT_EQ(5u, string_file.string().size());
+ EXPECT_EQ("vrtou", string_file.string());
+ EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_EQ(5, string_file.Seek(0, SEEK_END));
+ EXPECT_TRUE(string_file.Write("w", 1));
+ EXPECT_EQ(6u, string_file.string().size());
+ EXPECT_EQ("vrtouw", string_file.string());
+ EXPECT_EQ(6, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_EQ(8, string_file.Seek(2, SEEK_END));
+ EXPECT_EQ(6u, string_file.string().size());
+ EXPECT_EQ("vrtouw", string_file.string());
+
+ EXPECT_EQ(6, string_file.Seek(0, SEEK_END));
+ EXPECT_TRUE(string_file.Write("x", 1));
+ EXPECT_EQ(7u, string_file.string().size());
+ EXPECT_EQ("vrtouwx", string_file.string());
+ EXPECT_EQ(7, string_file.Seek(0, SEEK_CUR));
+}
+
+TEST(StringFile, SeekSparse) {
+ StringFile string_file;
+
+ EXPECT_EQ(3, string_file.Seek(3, SEEK_SET));
+ EXPECT_TRUE(string_file.string().empty());
+ EXPECT_EQ(3, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_TRUE(string_file.Write("abc", 3));
+ EXPECT_EQ(6u, string_file.string().size());
+ EXPECT_EQ(std::string("\0\0\0abc", 6), string_file.string());
+ EXPECT_EQ(6, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_EQ(9, string_file.Seek(3, SEEK_END));
+ EXPECT_EQ(6u, string_file.string().size());
+ EXPECT_EQ(9, string_file.Seek(0, SEEK_CUR));
+ char c;
+ EXPECT_EQ(0, string_file.Read(&c, 1));
+ EXPECT_EQ(9, string_file.Seek(0, SEEK_CUR));
+ EXPECT_EQ(6u, string_file.string().size());
+ EXPECT_TRUE(string_file.Write("def", 3));
+ EXPECT_EQ(12u, string_file.string().size());
+ EXPECT_EQ(std::string("\0\0\0abc\0\0\0def", 12), string_file.string());
+ EXPECT_EQ(12, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_EQ(7, string_file.Seek(-5, SEEK_END));
+ EXPECT_EQ(12u, string_file.string().size());
+ EXPECT_EQ(7, string_file.Seek(0, SEEK_CUR));
+ EXPECT_TRUE(string_file.Write("g", 1));
+ EXPECT_EQ(12u, string_file.string().size());
+ EXPECT_EQ(std::string("\0\0\0abc\0g\0def", 12), string_file.string());
+ EXPECT_EQ(8, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_EQ(15, string_file.Seek(7, SEEK_CUR));
+ EXPECT_EQ(12u, string_file.string().size());
+ EXPECT_EQ(15, string_file.Seek(0, SEEK_CUR));
+ EXPECT_TRUE(string_file.Write("hij", 3));
+ EXPECT_EQ(18u, string_file.string().size());
+ EXPECT_EQ(std::string("\0\0\0abc\0g\0def\0\0\0hij", 18),
+ string_file.string());
+ EXPECT_EQ(18, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_EQ(1, string_file.Seek(-17, SEEK_CUR));
+ EXPECT_EQ(18u, string_file.string().size());
+ EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR));
+ EXPECT_TRUE(string_file.Write("k", 1));
+ EXPECT_EQ(18u, string_file.string().size());
+ EXPECT_EQ(std::string("\0k\0abc\0g\0def\0\0\0hij", 18), string_file.string());
+ EXPECT_EQ(2, string_file.Seek(0, SEEK_CUR));
+
+ EXPECT_TRUE(string_file.Write("l", 1));
+ EXPECT_TRUE(string_file.Write("mnop", 4));
+ EXPECT_EQ(18u, string_file.string().size());
+ EXPECT_EQ(std::string("\0klmnopg\0def\0\0\0hij", 18), string_file.string());
+ EXPECT_EQ(7, string_file.Seek(0, SEEK_CUR));
+}
+
+TEST(StringFile, SeekInvalid) {
+ StringFile string_file;
+
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR));
+ EXPECT_EQ(1, string_file.Seek(1, SEEK_SET));
+ EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR));
+ EXPECT_LT(string_file.Seek(-1, SEEK_SET), 0);
+ EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR));
+ EXPECT_LT(string_file.Seek(std::numeric_limits<ssize_t>::min(), SEEK_SET), 0);
+ EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR));
+ EXPECT_LT(string_file.Seek(std::numeric_limits<FileOffset>::min(), SEEK_SET),
+ 0);
+ EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR));
+ EXPECT_TRUE(string_file.string().empty());
+
+ static_assert(SEEK_SET != 3 && SEEK_CUR != 3 && SEEK_END != 3,
+ "3 must be invalid for whence");
+ EXPECT_LT(string_file.Seek(0, 3), 0);
+
+ string_file.Reset();
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR));
+ EXPECT_TRUE(string_file.string().empty());
+
+ const FileOffset kMaxOffset = static_cast<FileOffset>(
+ std::min(implicit_cast<uint64_t>(std::numeric_limits<FileOffset>::max()),
+ implicit_cast<uint64_t>(std::numeric_limits<size_t>::max())));
+
+ EXPECT_EQ(kMaxOffset, string_file.Seek(kMaxOffset, SEEK_SET));
+ EXPECT_EQ(kMaxOffset, string_file.Seek(0, SEEK_CUR));
+ EXPECT_LT(string_file.Seek(1, SEEK_CUR), 0);
+
+ EXPECT_EQ(1, string_file.Seek(1, SEEK_SET));
+ EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR));
+ EXPECT_LT(string_file.Seek(kMaxOffset, SEEK_CUR), 0);
+}
+
+TEST(StringFile, SeekSet) {
+ StringFile string_file;
+ EXPECT_TRUE(string_file.SeekSet(1));
+ EXPECT_EQ(1, string_file.Seek(0, SEEK_CUR));
+ EXPECT_TRUE(string_file.SeekSet(0));
+ EXPECT_EQ(0, string_file.Seek(0, SEEK_CUR));
+ EXPECT_TRUE(string_file.SeekSet(10));
+ EXPECT_EQ(10, string_file.Seek(0, SEEK_CUR));
+ EXPECT_FALSE(string_file.SeekSet(-1));
+ EXPECT_EQ(10, string_file.Seek(0, SEEK_CUR));
+ EXPECT_FALSE(string_file.SeekSet(std::numeric_limits<ssize_t>::min()));
+ EXPECT_EQ(10, string_file.Seek(0, SEEK_CUR));
+ EXPECT_FALSE(string_file.SeekSet(std::numeric_limits<FileOffset>::min()));
+ EXPECT_EQ(10, string_file.Seek(0, SEEK_CUR));
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mac/checked_mach_address_range.h b/chromium/third_party/crashpad/crashpad/util/mac/checked_mach_address_range.h
new file mode 100644
index 00000000000..e64e3d1ec77
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mac/checked_mach_address_range.h
@@ -0,0 +1,38 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MAC_CHECKED_MACH_ADDRESS_RANGE_H_
+#define CRASHPAD_UTIL_MAC_CHECKED_MACH_ADDRESS_RANGE_H_
+
+#include <mach/mach.h>
+
+#include "util/numeric/checked_address_range.h"
+
+namespace crashpad {
+
+//! \brief Ensures that a range, composed of a base and a size, does not
+//! overflow the pointer type of the process it describes a range in.
+//!
+//! This class checks bases of type `mach_vm_address_t` and sizes of type
+//! `mach_vm_address_t` against a process whose pointer type is either 32 or 64
+//! bits wide.
+//!
+//! Aside from varying the overall range on the basis of a process’ pointer type
+//! width, this class functions very similarly to CheckedRange.
+using CheckedMachAddressRange =
+ internal::CheckedAddressRangeGeneric<mach_vm_address_t, mach_vm_size_t>;
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MAC_CHECKED_MACH_ADDRESS_RANGE_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/mac/checked_mach_address_range_test.cc b/chromium/third_party/crashpad/crashpad/util/mac/checked_mach_address_range_test.cc
new file mode 100644
index 00000000000..702715d092a
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mac/checked_mach_address_range_test.cc
@@ -0,0 +1,256 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mac/checked_mach_address_range.h"
+
+#include <mach/mach.h>
+
+#include <limits>
+
+#include "base/basictypes.h"
+#include "base/strings/stringprintf.h"
+#include "build/build_config.h"
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+enum Validity {
+ kInvalid = false,
+ kValid,
+ kValid64Invalid32,
+};
+
+bool ExpectationForValidity32(Validity validity) {
+ return validity == kValid;
+}
+
+bool ExpectationForValidity64(Validity validity) {
+ return validity == kValid || validity == kValid64Invalid32;
+}
+
+TEST(CheckedMachAddressRange, IsValid) {
+ const struct TestData {
+ mach_vm_address_t base;
+ mach_vm_size_t size;
+ Validity validity;
+ } kTestData[] = {
+ {0, 0, kValid},
+ {0, 1, kValid},
+ {0, 2, kValid},
+ {0, 0x7fffffff, kValid},
+ {0, 0x80000000, kValid},
+ {0, 0xfffffffe, kValid},
+ {0, 0xffffffff, kValid},
+ {0, 0xffffffffffffffff, kValid64Invalid32},
+ {1, 0, kValid},
+ {1, 1, kValid},
+ {1, 2, kValid},
+ {1, 0x7fffffff, kValid},
+ {1, 0x80000000, kValid},
+ {1, 0xfffffffe, kValid},
+ {1, 0xffffffff, kValid64Invalid32},
+ {1, 0xfffffffffffffffe, kValid64Invalid32},
+ {1, 0xffffffffffffffff, kInvalid},
+ {0x7fffffff, 0, kValid},
+ {0x7fffffff, 1, kValid},
+ {0x7fffffff, 2, kValid},
+ {0x7fffffff, 0x7fffffff, kValid},
+ {0x7fffffff, 0x80000000, kValid},
+ {0x7fffffff, 0xfffffffe, kValid64Invalid32},
+ {0x7fffffff, 0xffffffff, kValid64Invalid32},
+ {0x80000000, 0, kValid},
+ {0x80000000, 1, kValid},
+ {0x80000000, 2, kValid},
+ {0x80000000, 0x7fffffff, kValid},
+ {0x80000000, 0x80000000, kValid64Invalid32},
+ {0x80000000, 0xfffffffe, kValid64Invalid32},
+ {0x80000000, 0xffffffff, kValid64Invalid32},
+ {0xfffffffe, 0, kValid},
+ {0xfffffffe, 1, kValid},
+ {0xfffffffe, 2, kValid64Invalid32},
+ {0xfffffffe, 0x7fffffff, kValid64Invalid32},
+ {0xfffffffe, 0x80000000, kValid64Invalid32},
+ {0xfffffffe, 0xfffffffe, kValid64Invalid32},
+ {0xfffffffe, 0xffffffff, kValid64Invalid32},
+ {0xffffffff, 0, kValid},
+ {0xffffffff, 1, kValid64Invalid32},
+ {0xffffffff, 2, kValid64Invalid32},
+ {0xffffffff, 0x7fffffff, kValid64Invalid32},
+ {0xffffffff, 0x80000000, kValid64Invalid32},
+ {0xffffffff, 0xfffffffe, kValid64Invalid32},
+ {0xffffffff, 0xffffffff, kValid64Invalid32},
+ {0x7fffffffffffffff, 0, kValid64Invalid32},
+ {0x7fffffffffffffff, 1, kValid64Invalid32},
+ {0x7fffffffffffffff, 2, kValid64Invalid32},
+ {0x7fffffffffffffff, 0x7fffffffffffffff, kValid64Invalid32},
+ {0x7fffffffffffffff, 0x8000000000000000, kValid64Invalid32},
+ {0x7fffffffffffffff, 0x8000000000000001, kInvalid},
+ {0x7fffffffffffffff, 0xfffffffffffffffe, kInvalid},
+ {0x7fffffffffffffff, 0xffffffffffffffff, kInvalid},
+ {0x8000000000000000, 0, kValid64Invalid32},
+ {0x8000000000000000, 1, kValid64Invalid32},
+ {0x8000000000000000, 2, kValid64Invalid32},
+ {0x8000000000000000, 0x7fffffffffffffff, kValid64Invalid32},
+ {0x8000000000000000, 0x8000000000000000, kInvalid},
+ {0x8000000000000000, 0x8000000000000001, kInvalid},
+ {0x8000000000000000, 0xfffffffffffffffe, kInvalid},
+ {0x8000000000000000, 0xffffffffffffffff, kInvalid},
+ {0xfffffffffffffffe, 0, kValid64Invalid32},
+ {0xfffffffffffffffe, 1, kValid64Invalid32},
+ {0xfffffffffffffffe, 2, kInvalid},
+ {0xffffffffffffffff, 0, kValid64Invalid32},
+ {0xffffffffffffffff, 1, kInvalid},
+ };
+
+ for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ const TestData& testcase = kTestData[index];
+ SCOPED_TRACE(base::StringPrintf("index %zu, base 0x%llx, size 0x%llx",
+ index,
+ testcase.base,
+ testcase.size));
+
+ CheckedMachAddressRange range_32(false, testcase.base, testcase.size);
+ EXPECT_EQ(ExpectationForValidity32(testcase.validity), range_32.IsValid());
+
+ CheckedMachAddressRange range_64(true, testcase.base, testcase.size);
+ EXPECT_EQ(ExpectationForValidity64(testcase.validity), range_64.IsValid());
+ }
+}
+
+TEST(CheckedMachAddressRange, ContainsValue) {
+ const struct TestData {
+ mach_vm_address_t value;
+ bool expectation;
+ } kTestData[] = {
+ {0, false},
+ {1, false},
+ {0x1fff, false},
+ {0x2000, true},
+ {0x2001, true},
+ {0x2ffe, true},
+ {0x2fff, true},
+ {0x3000, false},
+ {0x3001, false},
+ {0x7fffffff, false},
+ {0x80000000, false},
+ {0x80000001, false},
+ {0x80001fff, false},
+ {0x80002000, false},
+ {0x80002001, false},
+ {0x80002ffe, false},
+ {0x80002fff, false},
+ {0x80003000, false},
+ {0x80003001, false},
+ {0xffffcfff, false},
+ {0xffffdfff, false},
+ {0xffffefff, false},
+ {0xffffffff, false},
+ {0x100000000, false},
+ {0xffffffffffffffff, false},
+ };
+
+ CheckedMachAddressRange parent_range_32(false, 0x2000, 0x1000);
+ ASSERT_TRUE(parent_range_32.IsValid());
+
+ for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ const TestData& testcase = kTestData[index];
+ SCOPED_TRACE(
+ base::StringPrintf("index %zu, value 0x%llx", index, testcase.value));
+
+ EXPECT_EQ(testcase.expectation,
+ parent_range_32.ContainsValue(testcase.value));
+ }
+
+ CheckedMachAddressRange parent_range_64(true, 0x100000000, 0x1000);
+ ASSERT_TRUE(parent_range_64.IsValid());
+ EXPECT_FALSE(parent_range_64.ContainsValue(0xffffffff));
+ EXPECT_TRUE(parent_range_64.ContainsValue(0x100000000));
+ EXPECT_TRUE(parent_range_64.ContainsValue(0x100000001));
+ EXPECT_TRUE(parent_range_64.ContainsValue(0x100000fff));
+ EXPECT_FALSE(parent_range_64.ContainsValue(0x100001000));
+}
+
+TEST(CheckedMachAddressRange, ContainsRange) {
+ const struct TestData {
+ mach_vm_address_t base;
+ mach_vm_size_t size;
+ bool expectation;
+ } kTestData[] = {
+ {0, 0, false},
+ {0, 1, false},
+ {0x2000, 0x1000, true},
+ {0, 0x2000, false},
+ {0x3000, 0x1000, false},
+ {0x1800, 0x1000, false},
+ {0x2800, 0x1000, false},
+ {0x2000, 0x800, true},
+ {0x2800, 0x800, true},
+ {0x2400, 0x800, true},
+ {0x2800, 0, true},
+ {0x2000, 0xffffdfff, false},
+ {0x2800, 0xffffd7ff, false},
+ {0x3000, 0xffffcfff, false},
+ {0xfffffffe, 1, false},
+ {0xffffffff, 0, false},
+ {0x1fff, 0, false},
+ {0x2000, 0, true},
+ {0x2001, 0, true},
+ {0x2fff, 0, true},
+ {0x3000, 0, true},
+ {0x3001, 0, false},
+ {0x1fff, 1, false},
+ {0x2000, 1, true},
+ {0x2001, 1, true},
+ {0x2fff, 1, true},
+ {0x3000, 1, false},
+ {0x3001, 1, false},
+ };
+
+ CheckedMachAddressRange parent_range_32(false, 0x2000, 0x1000);
+ ASSERT_TRUE(parent_range_32.IsValid());
+
+ for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ const TestData& testcase = kTestData[index];
+ SCOPED_TRACE(base::StringPrintf("index %zu, base 0x%llx, size 0x%llx",
+ index,
+ testcase.base,
+ testcase.size));
+
+ CheckedMachAddressRange child_range_32(false, testcase.base, testcase.size);
+ ASSERT_TRUE(child_range_32.IsValid());
+ EXPECT_EQ(testcase.expectation,
+ parent_range_32.ContainsRange(child_range_32));
+ }
+
+ CheckedMachAddressRange parent_range_64(true, 0x100000000, 0x1000);
+ ASSERT_TRUE(parent_range_64.IsValid());
+
+ CheckedMachAddressRange child_range_64(true, 0xffffffff, 2);
+ EXPECT_FALSE(parent_range_64.ContainsRange(child_range_64));
+
+ child_range_64.SetRange(true, 0x100000000, 2);
+ EXPECT_TRUE(parent_range_64.ContainsRange(child_range_64));
+
+ child_range_64.SetRange(true, 0x100000ffe, 2);
+ EXPECT_TRUE(parent_range_64.ContainsRange(child_range_64));
+
+ child_range_64.SetRange(true, 0x100000fff, 2);
+ EXPECT_FALSE(parent_range_64.ContainsRange(child_range_64));
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mac/launchd.h b/chromium/third_party/crashpad/crashpad/util/mac/launchd.h
new file mode 100644
index 00000000000..03f8bf0951c
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mac/launchd.h
@@ -0,0 +1,147 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MAC_LAUNCHD_H_
+#define CRASHPAD_UTIL_MAC_LAUNCHD_H_
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <launch.h>
+
+namespace crashpad {
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+//! \{
+//! \brief Wraps the `<launch.h>` function of the same name.
+//!
+//! The Mac OS X 10.10 SDK deprecates `<launch.h>`, although the functionality
+//! it provides is still useful. These wrappers allow the deprecated functions
+//! to be called without triggering deprecated-declaration warnings.
+
+inline launch_data_t LaunchDataAlloc(launch_data_type_t type) {
+ return launch_data_alloc(type);
+}
+
+inline launch_data_type_t LaunchDataGetType(const launch_data_t data) {
+ return launch_data_get_type(data);
+}
+
+inline void LaunchDataFree(launch_data_t data) {
+ return launch_data_free(data);
+}
+
+inline bool LaunchDataDictInsert(launch_data_t dict,
+ const launch_data_t value,
+ const char* key) {
+ return launch_data_dict_insert(dict, value, key);
+}
+
+inline launch_data_t LaunchDataDictLookup(const launch_data_t dict,
+ const char* key) {
+ return launch_data_dict_lookup(dict, key);
+}
+
+inline size_t LaunchDataDictGetCount(launch_data_t dict) {
+ return launch_data_dict_get_count(dict);
+}
+
+inline bool LaunchDataArraySetIndex(launch_data_t array,
+ const launch_data_t value,
+ size_t index) {
+ return launch_data_array_set_index(array, value, index);
+}
+
+inline launch_data_t LaunchDataArrayGetIndex(launch_data_t array,
+ size_t index) {
+ return launch_data_array_get_index(array, index);
+}
+
+inline size_t LaunchDataArrayGetCount(launch_data_t array) {
+ return launch_data_array_get_count(array);
+}
+
+inline launch_data_t LaunchDataNewInteger(long long integer) {
+ return launch_data_new_integer(integer);
+}
+
+inline launch_data_t LaunchDataNewBool(bool boolean) {
+ return launch_data_new_bool(boolean);
+}
+
+inline launch_data_t LaunchDataNewReal(double real) {
+ return launch_data_new_real(real);
+}
+
+inline launch_data_t LaunchDataNewString(const char* string) {
+ return launch_data_new_string(string);
+}
+
+inline launch_data_t LaunchDataNewOpaque(const void* opaque, size_t size) {
+ return launch_data_new_opaque(opaque, size);
+}
+
+inline long long LaunchDataGetInteger(const launch_data_t data) {
+ return launch_data_get_integer(data);
+}
+
+inline bool LaunchDataGetBool(const launch_data_t data) {
+ return launch_data_get_bool(data);
+}
+
+inline double LaunchDataGetReal(const launch_data_t data) {
+ return launch_data_get_real(data);
+}
+
+inline const char* LaunchDataGetString(const launch_data_t data) {
+ return launch_data_get_string(data);
+}
+
+inline void* LaunchDataGetOpaque(const launch_data_t data) {
+ return launch_data_get_opaque(data);
+}
+
+inline size_t LaunchDataGetOpaqueSize(const launch_data_t data) {
+ return launch_data_get_opaque_size(data);
+}
+
+inline int LaunchDataGetErrno(const launch_data_t data) {
+ return launch_data_get_errno(data);
+}
+
+inline launch_data_t LaunchMsg(const launch_data_t data) {
+ return launch_msg(data);
+}
+
+//! \}
+
+#pragma clang diagnostic pop
+
+//! \brief Converts a Core Foundation-type property list to a launchd-type
+//! `launch_data_t`.
+//!
+//! \param[in] property_cf The Core Foundation-type property list to convert.
+//!
+//! \return The converted launchd-type `launch_data_t`. The caller takes
+//! ownership of the returned value. On error, returns `nullptr`.
+//!
+//! \note This function handles all `CFPropertyListRef` types except for
+//! `CFDateRef`, because there’s no `launch_data_type_t` analogue. Not all
+//! types supported in a launchd-type `launch_data_t` have
+//! `CFPropertyListRef` analogues.
+launch_data_t CFPropertyToLaunchData(CFPropertyListRef property_cf);
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MAC_LAUNCHD_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/mac/launchd.mm b/chromium/third_party/crashpad/crashpad/util/mac/launchd.mm
new file mode 100644
index 00000000000..5d59cd2e390
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mac/launchd.mm
@@ -0,0 +1,141 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mac/launchd.h"
+
+#import <Foundation/Foundation.h>
+
+#include "base/basictypes.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/scoped_launch_data.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/strings/sys_string_conversions.h"
+
+namespace crashpad {
+
+launch_data_t CFPropertyToLaunchData(CFPropertyListRef property_cf) {
+ @autoreleasepool {
+ // This function mixes Foundation and Core Foundation access to property
+ // list elements according to which is more convenient and correct for any
+ // specific task.
+
+ launch_data_t data_launch = nullptr;
+ CFTypeID type_id_cf = CFGetTypeID(property_cf);
+
+ if (type_id_cf == CFDictionaryGetTypeID()) {
+ NSDictionary* dictionary_ns = base::mac::CFToNSCast(
+ base::mac::CFCastStrict<CFDictionaryRef>(property_cf));
+ base::mac::ScopedLaunchData dictionary_launch(
+ LaunchDataAlloc(LAUNCH_DATA_DICTIONARY));
+
+ for (NSString* key in dictionary_ns) {
+ if (![key isKindOfClass:[NSString class]]) {
+ return nullptr;
+ }
+
+ CFPropertyListRef value_cf =
+ implicit_cast<CFPropertyListRef>([dictionary_ns objectForKey:key]);
+ launch_data_t value_launch = CFPropertyToLaunchData(value_cf);
+ if (!value_launch) {
+ return nullptr;
+ }
+
+ LaunchDataDictInsert(dictionary_launch, value_launch, [key UTF8String]);
+ }
+
+ data_launch = dictionary_launch.release();
+
+ } else if (type_id_cf == CFArrayGetTypeID()) {
+ NSArray* array_ns = base::mac::CFToNSCast(
+ base::mac::CFCastStrict<CFArrayRef>(property_cf));
+ base::mac::ScopedLaunchData array_launch(
+ LaunchDataAlloc(LAUNCH_DATA_ARRAY));
+ size_t index = 0;
+
+ for (id element_ns in array_ns) {
+ CFPropertyListRef element_cf =
+ implicit_cast<CFPropertyListRef>(element_ns);
+ launch_data_t element_launch = CFPropertyToLaunchData(element_cf);
+ if (!element_launch) {
+ return nullptr;
+ }
+
+ LaunchDataArraySetIndex(array_launch, element_launch, index++);
+ }
+
+ data_launch = array_launch.release();
+
+ } else if (type_id_cf == CFNumberGetTypeID()) {
+ CFNumberRef number_cf = base::mac::CFCastStrict<CFNumberRef>(property_cf);
+ NSNumber* number_ns = base::mac::CFToNSCast(number_cf);
+ switch (CFNumberGetType(number_cf)) {
+ case kCFNumberSInt8Type:
+ case kCFNumberSInt16Type:
+ case kCFNumberSInt32Type:
+ case kCFNumberSInt64Type:
+ case kCFNumberCharType:
+ case kCFNumberShortType:
+ case kCFNumberIntType:
+ case kCFNumberLongType:
+ case kCFNumberLongLongType:
+ case kCFNumberCFIndexType:
+ case kCFNumberNSIntegerType: {
+ data_launch = LaunchDataNewInteger([number_ns longLongValue]);
+ break;
+ }
+
+ case kCFNumberFloat32Type:
+ case kCFNumberFloat64Type:
+ case kCFNumberFloatType:
+ case kCFNumberDoubleType: {
+ data_launch = LaunchDataNewReal([number_ns doubleValue]);
+ break;
+ }
+
+ default: { return nullptr; }
+ }
+
+ } else if (type_id_cf == CFBooleanGetTypeID()) {
+ CFBooleanRef boolean_cf =
+ base::mac::CFCastStrict<CFBooleanRef>(property_cf);
+ data_launch = LaunchDataNewBool(CFBooleanGetValue(boolean_cf));
+
+ } else if (type_id_cf == CFStringGetTypeID()) {
+ NSString* string_ns = base::mac::CFToNSCast(
+ base::mac::CFCastStrict<CFStringRef>(property_cf));
+
+ // -fileSystemRepresentation might be more correct than -UTF8String,
+ // because these strings can hold paths. The analogous function in
+ // launchctl, CF2launch_data() (10.9.4
+ // launchd-842.92.1/support/launchctl.c), uses UTF-8 instead of filesystem
+ // encoding, so do the same here. Note that there’s another occurrence of
+ // -UTF8String above, used for dictionary keys.
+ data_launch = LaunchDataNewString([string_ns UTF8String]);
+
+ } else if (type_id_cf == CFDataGetTypeID()) {
+ NSData* data_ns = base::mac::CFToNSCast(
+ base::mac::CFCastStrict<CFDataRef>(property_cf));
+ data_launch = LaunchDataNewOpaque([data_ns bytes], [data_ns length]);
+ } else {
+ base::ScopedCFTypeRef<CFStringRef> type_name_cf(
+ CFCopyTypeIDDescription(type_id_cf));
+ DLOG(ERROR) << "unable to convert CFTypeID " << type_id_cf << " ("
+ << base::SysCFStringRefToUTF8(type_name_cf) << ")";
+ }
+
+ return data_launch;
+ }
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mac/launchd_test.mm b/chromium/third_party/crashpad/crashpad/util/mac/launchd_test.mm
new file mode 100644
index 00000000000..65722a42cba
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mac/launchd_test.mm
@@ -0,0 +1,302 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mac/launchd.h"
+
+#import <Foundation/Foundation.h>
+#include <launch.h>
+#include <string.h>
+
+#include <cmath>
+#include <limits>
+
+#include "base/basictypes.h"
+#include "base/mac/scoped_launch_data.h"
+#include "gtest/gtest.h"
+#include "util/stdlib/objc.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(Launchd, CFPropertyToLaunchData_Integer) {
+ @autoreleasepool {
+ base::mac::ScopedLaunchData launch_data;
+
+ NSNumber* integer_nses[] = {
+ @0,
+ @1,
+ @-1,
+ @0x7f,
+ @0x80,
+ @0xff,
+ @0x0100,
+ @0x7fff,
+ @0x8000,
+ @0xffff,
+ @0x00010000,
+ @0x7fffffff,
+ @0x80000000,
+ @0xffffffff,
+ @0x1000000000000000,
+ @0x7fffffffffffffff,
+ @0x8000000000000000,
+ @0xffffffffffffffff,
+ @0x0123456789abcdef,
+ @0xfedcba9876543210,
+ };
+
+ for (size_t index = 0; index < arraysize(integer_nses); ++index) {
+ NSNumber* integer_ns = integer_nses[index];
+ launch_data.reset(CFPropertyToLaunchData(integer_ns));
+ ASSERT_TRUE(launch_data.get());
+ ASSERT_EQ(LAUNCH_DATA_INTEGER, LaunchDataGetType(launch_data));
+ EXPECT_EQ([integer_ns longLongValue], LaunchDataGetInteger(launch_data))
+ << "index " << index;
+ }
+ }
+}
+
+TEST(Launchd, CFPropertyToLaunchData_FloatingPoint) {
+ @autoreleasepool {
+ base::mac::ScopedLaunchData launch_data;
+
+ NSNumber* double_nses[] = {
+ @0.0,
+ @1.0,
+ @-1.0,
+ [NSNumber numberWithFloat:std::numeric_limits<float>::min()],
+ [NSNumber numberWithFloat:std::numeric_limits<float>::max()],
+ [NSNumber numberWithFloat:std::numeric_limits<double>::min()],
+ [NSNumber numberWithFloat:std::numeric_limits<double>::max()],
+ @3.1415926535897932,
+ [NSNumber numberWithFloat:std::numeric_limits<double>::infinity()],
+ [NSNumber numberWithFloat:std::numeric_limits<double>::quiet_NaN()],
+ [NSNumber numberWithFloat:std::numeric_limits<double>::signaling_NaN()],
+ };
+
+ for (size_t index = 0; index < arraysize(double_nses); ++index) {
+ NSNumber* double_ns = double_nses[index];
+ launch_data.reset(CFPropertyToLaunchData(double_ns));
+ ASSERT_TRUE(launch_data.get());
+ ASSERT_EQ(LAUNCH_DATA_REAL, LaunchDataGetType(launch_data));
+ double expected_double_value = [double_ns doubleValue];
+ double observed_double_value = LaunchDataGetReal(launch_data);
+ bool expected_is_nan = std::isnan(expected_double_value);
+ EXPECT_EQ(expected_is_nan, std::isnan(observed_double_value));
+ if (!expected_is_nan) {
+ EXPECT_DOUBLE_EQ(expected_double_value, observed_double_value)
+ << "index " << index;
+ }
+ }
+ }
+}
+
+TEST(Launchd, CFPropertyToLaunchData_Boolean) {
+ @autoreleasepool {
+ base::mac::ScopedLaunchData launch_data;
+
+ NSNumber* bool_nses[] = {
+ @NO,
+ @YES,
+ };
+
+ for (size_t index = 0; index < arraysize(bool_nses); ++index) {
+ NSNumber* bool_ns = bool_nses[index];
+ launch_data.reset(CFPropertyToLaunchData(bool_ns));
+ ASSERT_TRUE(launch_data.get());
+ ASSERT_EQ(LAUNCH_DATA_BOOL, LaunchDataGetType(launch_data));
+ if ([bool_ns boolValue]) {
+ EXPECT_TRUE(LaunchDataGetBool(launch_data));
+ } else {
+ EXPECT_FALSE(LaunchDataGetBool(launch_data));
+ }
+ }
+ }
+}
+
+TEST(Launchd, CFPropertyToLaunchData_String) {
+ @autoreleasepool {
+ base::mac::ScopedLaunchData launch_data;
+
+ NSString* string_nses[] = {
+ @"",
+ @"string",
+ @"Üñîçø∂é",
+ };
+
+ for (size_t index = 0; index < arraysize(string_nses); ++index) {
+ NSString* string_ns = string_nses[index];
+ launch_data.reset(CFPropertyToLaunchData(string_ns));
+ ASSERT_TRUE(launch_data.get());
+ ASSERT_EQ(LAUNCH_DATA_STRING, LaunchDataGetType(launch_data));
+ EXPECT_STREQ([string_ns UTF8String], LaunchDataGetString(launch_data));
+ }
+ }
+}
+
+TEST(Launchd, CFPropertyToLaunchData_Data) {
+ @autoreleasepool {
+ base::mac::ScopedLaunchData launch_data;
+
+ const uint8_t data_c[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 9, 8, 7, 6, 5, 4, 3, 2};
+ NSData* data_ns = [NSData dataWithBytes:data_c length:sizeof(data_c)];
+ launch_data.reset(CFPropertyToLaunchData(data_ns));
+ ASSERT_TRUE(launch_data.get());
+ ASSERT_EQ(LAUNCH_DATA_OPAQUE, LaunchDataGetType(launch_data));
+ EXPECT_EQ(sizeof(data_c), LaunchDataGetOpaqueSize(launch_data));
+ EXPECT_EQ(0,
+ memcmp(LaunchDataGetOpaque(launch_data), data_c, sizeof(data_c)));
+ }
+}
+
+TEST(Launchd, CFPropertyToLaunchData_Dictionary) {
+ @autoreleasepool {
+ base::mac::ScopedLaunchData launch_data;
+
+ NSDictionary* dictionary_ns = @{
+ @"key" : @"value",
+ };
+
+ launch_data.reset(CFPropertyToLaunchData(dictionary_ns));
+ ASSERT_TRUE(launch_data.get());
+ ASSERT_EQ(LAUNCH_DATA_DICTIONARY, LaunchDataGetType(launch_data));
+ EXPECT_EQ([dictionary_ns count], LaunchDataDictGetCount(launch_data));
+
+ launch_data_t launch_lookup_data = LaunchDataDictLookup(launch_data, "key");
+ ASSERT_TRUE(launch_lookup_data);
+ ASSERT_EQ(LAUNCH_DATA_STRING, LaunchDataGetType(launch_lookup_data));
+ EXPECT_STREQ("value", LaunchDataGetString(launch_lookup_data));
+ }
+}
+
+TEST(Launchd, CFPropertyToLaunchData_Array) {
+ @autoreleasepool {
+ base::mac::ScopedLaunchData launch_data;
+
+ NSArray* array_ns = @[ @"element_1", @"element_2", ];
+
+ launch_data.reset(CFPropertyToLaunchData(array_ns));
+ ASSERT_TRUE(launch_data.get());
+ ASSERT_EQ(LAUNCH_DATA_ARRAY, LaunchDataGetType(launch_data));
+ EXPECT_EQ([array_ns count], LaunchDataArrayGetCount(launch_data));
+
+ launch_data_t launch_lookup_data = LaunchDataArrayGetIndex(launch_data, 0);
+ ASSERT_TRUE(launch_lookup_data);
+ ASSERT_EQ(LAUNCH_DATA_STRING, LaunchDataGetType(launch_lookup_data));
+ EXPECT_STREQ("element_1", LaunchDataGetString(launch_lookup_data));
+
+ launch_lookup_data = LaunchDataArrayGetIndex(launch_data, 1);
+ ASSERT_TRUE(launch_lookup_data);
+ ASSERT_EQ(LAUNCH_DATA_STRING, LaunchDataGetType(launch_lookup_data));
+ EXPECT_STREQ("element_2", LaunchDataGetString(launch_lookup_data));
+ }
+}
+
+TEST(Launchd, CFPropertyToLaunchData_NSDate) {
+ // Test that NSDate conversions fail as advertised. There’s no type for
+ // storing date values in a launch_data_t.
+
+ @autoreleasepool {
+ base::mac::ScopedLaunchData launch_data;
+
+ NSDate* date = [NSDate date];
+ launch_data.reset(CFPropertyToLaunchData(date));
+ EXPECT_FALSE(launch_data);
+
+ NSDictionary* date_dictionary = @{
+ @"key" : @"value",
+ @"date" : date,
+ };
+ launch_data.reset(CFPropertyToLaunchData(date_dictionary));
+ EXPECT_FALSE(launch_data);
+
+ NSArray* date_array = @[ @"string_1", date, @"string_2", ];
+ launch_data.reset(CFPropertyToLaunchData(date_array));
+ EXPECT_FALSE(launch_data);
+ }
+}
+
+TEST(Launchd, CFPropertyToLaunchData_RealWorldJobDictionary) {
+ @autoreleasepool {
+ base::mac::ScopedLaunchData launch_data;
+
+ NSDictionary* job_dictionary = @{
+ @LAUNCH_JOBKEY_LABEL : @"com.example.job.rebooter",
+ @LAUNCH_JOBKEY_ONDEMAND : @YES,
+ @LAUNCH_JOBKEY_PROGRAMARGUMENTS :
+ @[ @"/bin/bash", @"-c", @"/sbin/reboot", ],
+ @LAUNCH_JOBKEY_MACHSERVICES : @{
+ @"com.example.service.rebooter" : @YES,
+ },
+ };
+
+ launch_data.reset(CFPropertyToLaunchData(job_dictionary));
+ ASSERT_TRUE(launch_data.get());
+ ASSERT_EQ(LAUNCH_DATA_DICTIONARY, LaunchDataGetType(launch_data));
+ EXPECT_EQ(4u, LaunchDataDictGetCount(launch_data));
+
+ launch_data_t launch_lookup_data =
+ LaunchDataDictLookup(launch_data, LAUNCH_JOBKEY_LABEL);
+ ASSERT_TRUE(launch_lookup_data);
+ ASSERT_EQ(LAUNCH_DATA_STRING, LaunchDataGetType(launch_lookup_data));
+ EXPECT_STREQ("com.example.job.rebooter",
+ LaunchDataGetString(launch_lookup_data));
+
+ launch_lookup_data =
+ LaunchDataDictLookup(launch_data, LAUNCH_JOBKEY_ONDEMAND);
+ ASSERT_TRUE(launch_lookup_data);
+ ASSERT_EQ(LAUNCH_DATA_BOOL, LaunchDataGetType(launch_lookup_data));
+ EXPECT_TRUE(LaunchDataGetBool(launch_lookup_data));
+
+ launch_lookup_data =
+ LaunchDataDictLookup(launch_data, LAUNCH_JOBKEY_PROGRAMARGUMENTS);
+ ASSERT_TRUE(launch_lookup_data);
+ ASSERT_EQ(LAUNCH_DATA_ARRAY, LaunchDataGetType(launch_lookup_data));
+ EXPECT_EQ(3u, LaunchDataArrayGetCount(launch_lookup_data));
+
+ launch_data_t launch_sublookup_data =
+ LaunchDataArrayGetIndex(launch_lookup_data, 0);
+ ASSERT_TRUE(launch_sublookup_data);
+ ASSERT_EQ(LAUNCH_DATA_STRING, LaunchDataGetType(launch_sublookup_data));
+ EXPECT_STREQ("/bin/bash", LaunchDataGetString(launch_sublookup_data));
+
+ launch_sublookup_data = LaunchDataArrayGetIndex(launch_lookup_data, 1);
+ ASSERT_TRUE(launch_sublookup_data);
+ ASSERT_EQ(LAUNCH_DATA_STRING, LaunchDataGetType(launch_sublookup_data));
+ EXPECT_STREQ("-c", LaunchDataGetString(launch_sublookup_data));
+
+ launch_sublookup_data = LaunchDataArrayGetIndex(launch_lookup_data, 2);
+ ASSERT_TRUE(launch_sublookup_data);
+ ASSERT_EQ(LAUNCH_DATA_STRING, LaunchDataGetType(launch_sublookup_data));
+ EXPECT_STREQ("/sbin/reboot", LaunchDataGetString(launch_sublookup_data));
+
+ launch_lookup_data =
+ LaunchDataDictLookup(launch_data, LAUNCH_JOBKEY_MACHSERVICES);
+ ASSERT_TRUE(launch_lookup_data);
+ ASSERT_EQ(LAUNCH_DATA_DICTIONARY, LaunchDataGetType(launch_lookup_data));
+ EXPECT_EQ(1u, LaunchDataDictGetCount(launch_lookup_data));
+
+ launch_sublookup_data = LaunchDataDictLookup(
+ launch_lookup_data, "com.example.service.rebooter");
+ ASSERT_TRUE(launch_sublookup_data);
+ ASSERT_EQ(LAUNCH_DATA_BOOL, LaunchDataGetType(launch_sublookup_data));
+ EXPECT_TRUE(LaunchDataGetBool(launch_sublookup_data));
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mac/mac_util.cc b/chromium/third_party/crashpad/crashpad/util/mac/mac_util.cc
new file mode 100644
index 00000000000..a792e511974
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mac/mac_util.cc
@@ -0,0 +1,282 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mac/mac_util.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/mac/scoped_ioobject.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/sys_string_conversions.h"
+
+extern "C" {
+// Private CoreFoundation internals. See 10.9.2 CF-855.14/CFPriv.h and
+// CF-855.14/CFUtilities.c. These are marked for weak import because they’re
+// private and subject to change.
+
+#define WEAK_IMPORT __attribute__((weak_import))
+
+// Don’t call these functions directly, call them through the
+// TryCFCopy*VersionDictionary() helpers to account for the possibility that
+// they may not be present at runtime.
+CFDictionaryRef _CFCopySystemVersionDictionary() WEAK_IMPORT;
+CFDictionaryRef _CFCopyServerVersionDictionary() WEAK_IMPORT;
+
+// Don’t use these constants with CFDictionaryGetValue() directly, use them with
+// the TryCFDictionaryGetValue() wrapper to account for the possibility that
+// they may not be present at runtime.
+extern const CFStringRef _kCFSystemVersionProductNameKey WEAK_IMPORT;
+extern const CFStringRef _kCFSystemVersionProductVersionKey WEAK_IMPORT;
+extern const CFStringRef _kCFSystemVersionProductVersionExtraKey WEAK_IMPORT;
+extern const CFStringRef _kCFSystemVersionBuildVersionKey WEAK_IMPORT;
+
+#undef WEAK_IMPORT
+
+} // extern "C"
+
+namespace {
+
+// Returns the running system’s Darwin major version. Don’t call this, it’s an
+// implementation detail and its result is meant to be cached by
+// MacOSXMinorVersion().
+//
+// This is very similar to Chromium’s base/mac/mac_util.mm
+// DarwinMajorVersionInternal().
+int DarwinMajorVersion() {
+ // base::OperatingSystemVersionNumbers calls Gestalt(), which is a
+ // higher-level function than is needed. It might perform unnecessary
+ // operations. On 10.6, it was observed to be able to spawn threads (see
+ // http://crbug.com/53200). It might also read files or perform other blocking
+ // operations. Actually, nobody really knows for sure just what Gestalt()
+ // might do, or what it might be taught to do in the future.
+ //
+ // uname(), on the other hand, is implemented as a simple series of sysctl()
+ // system calls to obtain the relevant data from the kernel. The data is
+ // compiled right into the kernel, so no threads or blocking or other funny
+ // business is necessary.
+
+ utsname uname_info;
+ int rv = uname(&uname_info);
+ PCHECK(rv == 0) << "uname";
+
+ DCHECK_EQ(strcmp(uname_info.sysname, "Darwin"), 0) << "unexpected sysname "
+ << uname_info.sysname;
+
+ char* dot = strchr(uname_info.release, '.');
+ CHECK(dot);
+
+ int darwin_major_version = 0;
+ CHECK(base::StringToInt(
+ base::StringPiece(uname_info.release, dot - uname_info.release),
+ &darwin_major_version));
+
+ return darwin_major_version;
+}
+
+// Helpers for the weak-imported private CoreFoundation internals.
+
+CFDictionaryRef TryCFCopySystemVersionDictionary() {
+ if (_CFCopySystemVersionDictionary) {
+ return _CFCopySystemVersionDictionary();
+ }
+ return nullptr;
+}
+
+CFDictionaryRef TryCFCopyServerVersionDictionary() {
+ if (_CFCopyServerVersionDictionary) {
+ return _CFCopyServerVersionDictionary();
+ }
+ return nullptr;
+}
+
+const void* TryCFDictionaryGetValue(CFDictionaryRef dictionary,
+ const void* value) {
+ if (value) {
+ return CFDictionaryGetValue(dictionary, value);
+ }
+ return nullptr;
+}
+
+// Converts |version| to a triplet of version numbers on behalf of
+// MacOSXVersion(). Returns true on success. If |version| does not have the
+// expected format, returns false. |version| must be in the form "10.9.2" or
+// just "10.9". In the latter case, |bugfix| will be set to 0.
+bool StringToVersionNumbers(const std::string& version,
+ int* major,
+ int* minor,
+ int* bugfix) {
+ size_t first_dot = version.find_first_of('.');
+ if (first_dot == 0 || first_dot == std::string::npos ||
+ first_dot == version.length() - 1) {
+ LOG(ERROR) << "version has unexpected format";
+ return false;
+ }
+ if (!base::StringToInt(base::StringPiece(&version[0], first_dot), major)) {
+ LOG(ERROR) << "version has unexpected format";
+ return false;
+ }
+
+ size_t second_dot = version.find_first_of('.', first_dot + 1);
+ if (second_dot == version.length() - 1) {
+ LOG(ERROR) << "version has unexpected format";
+ return false;
+ } else if (second_dot == std::string::npos) {
+ second_dot = version.length();
+ }
+
+ if (!base::StringToInt(base::StringPiece(&version[first_dot + 1],
+ second_dot - first_dot - 1),
+ minor)) {
+ LOG(ERROR) << "version has unexpected format";
+ return false;
+ }
+
+ if (second_dot == version.length()) {
+ *bugfix = 0;
+ } else if (!base::StringToInt(
+ base::StringPiece(&version[second_dot + 1],
+ version.length() - second_dot - 1),
+ bugfix)) {
+ LOG(ERROR) << "version has unexpected format";
+ return false;
+ }
+
+ return true;
+}
+
+std::string IORegistryEntryDataPropertyAsString(io_registry_entry_t entry,
+ CFStringRef key) {
+ base::ScopedCFTypeRef<CFTypeRef> property(
+ IORegistryEntryCreateCFProperty(entry, key, kCFAllocatorDefault, 0));
+ CFDataRef data = base::mac::CFCast<CFDataRef>(property);
+ if (data && CFDataGetLength(data) > 0) {
+ return reinterpret_cast<const char*>(CFDataGetBytePtr(data));
+ }
+
+ return std::string();
+}
+
+} // namespace
+
+namespace crashpad {
+
+int MacOSXMinorVersion() {
+ // The Darwin major version is always 4 greater than the Mac OS X minor
+ // version for Darwin versions beginning with 6, corresponding to Mac OS X
+ // 10.2.
+ static int mac_os_x_minor_version = DarwinMajorVersion() - 4;
+ DCHECK(mac_os_x_minor_version >= 2);
+ return mac_os_x_minor_version;
+}
+
+bool MacOSXVersion(int* major,
+ int* minor,
+ int* bugfix,
+ std::string* build,
+ bool* server,
+ std::string* version_string) {
+ base::ScopedCFTypeRef<CFDictionaryRef> dictionary(
+ TryCFCopyServerVersionDictionary());
+ if (dictionary) {
+ *server = true;
+ } else {
+ dictionary.reset(TryCFCopySystemVersionDictionary());
+ if (!dictionary) {
+ LOG(ERROR) << "_CFCopySystemVersionDictionary failed";
+ return false;
+ }
+ *server = false;
+ }
+
+ bool success = true;
+
+ CFStringRef version_cf = base::mac::CFCast<CFStringRef>(
+ TryCFDictionaryGetValue(dictionary, _kCFSystemVersionProductVersionKey));
+ std::string version;
+ if (!version_cf) {
+ LOG(ERROR) << "version_cf not found";
+ success = false;
+ } else {
+ version = base::SysCFStringRefToUTF8(version_cf);
+ success &= StringToVersionNumbers(version, major, minor, bugfix);
+ }
+
+ CFStringRef build_cf = base::mac::CFCast<CFStringRef>(
+ TryCFDictionaryGetValue(dictionary, _kCFSystemVersionBuildVersionKey));
+ if (!build_cf) {
+ LOG(ERROR) << "build_cf not found";
+ success = false;
+ } else {
+ build->assign(base::SysCFStringRefToUTF8(build_cf));
+ }
+
+ CFStringRef product_cf = base::mac::CFCast<CFStringRef>(
+ TryCFDictionaryGetValue(dictionary, _kCFSystemVersionProductNameKey));
+ std::string product;
+ if (!product_cf) {
+ LOG(ERROR) << "product_cf not found";
+ success = false;
+ } else {
+ product = base::SysCFStringRefToUTF8(product_cf);
+ }
+
+ // This key is not required, and in fact is normally not present.
+ CFStringRef extra_cf = base::mac::CFCast<CFStringRef>(TryCFDictionaryGetValue(
+ dictionary, _kCFSystemVersionProductVersionExtraKey));
+ std::string extra;
+ if (extra_cf) {
+ extra = base::SysCFStringRefToUTF8(extra_cf);
+ }
+
+ if (!product.empty() || !version.empty() || !build->empty()) {
+ if (!extra.empty()) {
+ version_string->assign(base::StringPrintf("%s %s %s (%s)",
+ product.c_str(),
+ version.c_str(),
+ extra.c_str(),
+ build->c_str()));
+ } else {
+ version_string->assign(base::StringPrintf(
+ "%s %s (%s)", product.c_str(), version.c_str(), build->c_str()));
+ }
+ }
+
+ return success;
+}
+
+void MacModelAndBoard(std::string* model, std::string* board_id) {
+ base::mac::ScopedIOObject<io_service_t> platform_expert(
+ IOServiceGetMatchingService(kIOMasterPortDefault,
+ IOServiceMatching("IOPlatformExpertDevice")));
+ if (platform_expert) {
+ model->assign(
+ IORegistryEntryDataPropertyAsString(platform_expert, CFSTR("model")));
+ board_id->assign(IORegistryEntryDataPropertyAsString(platform_expert,
+ CFSTR("board-id")));
+ } else {
+ model->clear();
+ board_id->clear();
+ }
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mac/mac_util.h b/chromium/third_party/crashpad/crashpad/util/mac/mac_util.h
new file mode 100644
index 00000000000..9cedfdfb2ca
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mac/mac_util.h
@@ -0,0 +1,73 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MAC_MAC_UTIL_H_
+#define CRASHPAD_UTIL_MAC_MAC_UTIL_H_
+
+#include <string>
+
+namespace crashpad {
+
+//! \brief Returns the version of the running operating system.
+//!
+//! \return The minor version of the operating system, such as `9` for Mac OS X
+//! 10.9.2.
+//!
+//! \note This is similar to the base::mac::IsOS*() family of functions, but
+//! is provided for situations where the caller needs to obtain version
+//! information beyond what is provided by Chromium’s base, or for when the
+//! caller needs the actual minor version value.
+int MacOSXMinorVersion();
+
+//! \brief Returns the version of the running operating system.
+//!
+//! All parameters are required. No parameter may be `nullptr`.
+//!
+//! \param[out] major The major version of the operating system, such as `10`
+//! for Mac OS X 10.9.2.
+//! \param[out] minor The major version of the operating system, such as `9` for
+//! Mac OS X 10.9.2.
+//! \param[out] bugfix The bugfix version of the operating system, such as `2`
+//! for Mac OS X 10.9.2.
+//! \param[out] build The operating system’s build string, such as "13C64" for
+//! Mac OS X 10.9.2.
+//! \param[out] server `true` for a Mac OS X Server installation, `false`
+//! otherwise (for a desktop/laptop, client, or workstation system).
+//! \param[out] version_string A string representing the full operating system
+//! version, such as `"Mac OS X 10.9.2 (13C64)"`.
+//!
+//! \return `true` on success, `false` on failure, with an error message logged.
+//! A failure is considered to have occurred if any element could not be
+//! determined. When this happens, their values will be untouched, but other
+//! values that could be determined will still be set properly.
+bool MacOSXVersion(int* major,
+ int* minor,
+ int* bugfix,
+ std::string* build,
+ bool* server,
+ std::string* version_string);
+
+//! \brief Returns the model name and board ID of the running system.
+//!
+//! \param[out] model The system’s model name. A mid-2012 15" MacBook Pro would
+//! report “MacBookPro10,1”.
+//! \param[out] board_id The system’s board ID. A mid-2012 15" MacBook Pro would
+//! report “Mac-C3EC7CD22292981F”.
+//!
+//! If a value cannot be determined, its string is cleared.
+void MacModelAndBoard(std::string* model, std::string* board_id);
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MAC_MAC_UTIL_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/mac/mac_util_test.mm b/chromium/third_party/crashpad/crashpad/util/mac/mac_util_test.mm
new file mode 100644
index 00000000000..5b41b4209ec
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mac/mac_util_test.mm
@@ -0,0 +1,143 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mac/mac_util.h"
+
+#import <Foundation/Foundation.h>
+#include <stdlib.h>
+
+#include <string>
+
+#include "base/mac/scoped_nsobject.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+
+#ifdef __GLIBCXX__
+// When C++ exceptions are disabled, libstdc++ from GCC 4.2 defines |try| and
+// |catch| so as to allow exception-expecting C++ code to build properly when
+// language support for exceptions is not present. These macros interfere with
+// the use of |@try| and |@catch| in Objective-C files such as this one.
+// Undefine these macros here, after everything has been #included, since there
+// will be no C++ uses and only Objective-C uses from this point on.
+#undef try
+#undef catch
+#endif
+
+namespace crashpad {
+namespace test {
+namespace {
+
+// Runs /usr/bin/sw_vers with a single argument, |argument|, and places the
+// command’s standard output into |output| after stripping the trailing newline.
+// Fatal gtest assertions report tool failures, which the caller should check
+// for with ASSERT_NO_FATAL_FAILURE() or testing::Test::HasFatalFailure().
+void SwVers(NSString* argument, std::string* output) {
+ @autoreleasepool {
+ base::scoped_nsobject<NSPipe> pipe([[NSPipe alloc] init]);
+ base::scoped_nsobject<NSTask> task([[NSTask alloc] init]);
+ [task setStandardOutput:pipe];
+ [task setLaunchPath:@"/usr/bin/sw_vers"];
+ [task setArguments:@[ argument ]];
+
+ @try {
+ [task launch];
+ }
+ @catch (NSException* exception) {
+ FAIL() << [[exception name] UTF8String] << ": "
+ << [[exception reason] UTF8String];
+ }
+
+ NSData* data = [[pipe fileHandleForReading] readDataToEndOfFile];
+ [task waitUntilExit];
+
+ ASSERT_EQ(NSTaskTerminationReasonExit, [task terminationReason]);
+ ASSERT_EQ(EXIT_SUCCESS, [task terminationStatus]);
+
+ output->assign(reinterpret_cast<const char*>([data bytes]), [data length]);
+
+ EXPECT_EQ('\n', output->at(output->size() - 1));
+ output->resize(output->size() - 1);
+ }
+}
+
+TEST(MacUtil, MacOSXVersion) {
+ int major;
+ int minor;
+ int bugfix;
+ std::string build;
+ bool server;
+ std::string version_string;
+ ASSERT_TRUE(
+ MacOSXVersion(&major, &minor, &bugfix, &build, &server, &version_string));
+
+ std::string version;
+ if (bugfix) {
+ version = base::StringPrintf("%d.%d.%d", major, minor, bugfix);
+ } else {
+ // 10.x.0 releases report their version string as simply 10.x.
+ version = base::StringPrintf("%d.%d", major, minor);
+ }
+
+ std::string expected_product_version;
+ ASSERT_NO_FATAL_FAILURE(
+ SwVers(@"-productVersion", &expected_product_version));
+
+ EXPECT_EQ(expected_product_version, version);
+
+ std::string expected_build_version;
+ ASSERT_NO_FATAL_FAILURE(SwVers(@"-buildVersion", &expected_build_version));
+
+ EXPECT_EQ(expected_build_version, build);
+
+ std::string expected_product_name;
+ ASSERT_NO_FATAL_FAILURE(SwVers(@"-productName", &expected_product_name));
+
+ // Look for a space after the product name in the complete version string.
+ expected_product_name += ' ';
+ EXPECT_EQ(0u, version_string.find(expected_product_name));
+}
+
+TEST(MacUtil, MacOSXMinorVersion) {
+ // Make sure that MacOSXMinorVersion() and MacOSXVersion() agree. The two have
+ // their own distinct implementations, and the latter was checked against
+ // sw_vers above.
+ int major;
+ int minor;
+ int bugfix;
+ std::string build;
+ bool server;
+ std::string version_string;
+ ASSERT_TRUE(
+ MacOSXVersion(&major, &minor, &bugfix, &build, &server, &version_string));
+
+ EXPECT_EQ(minor, MacOSXMinorVersion());
+}
+
+TEST(MacUtil, MacModelAndBoard) {
+ // There’s not much that can be done to test these, so just make sure they’re
+ // not empty. The model could be compared against the parsed output of
+ // “system_profiler SPHardwareDataType”, but the board doesn’t show up
+ // anywhere other than the I/O Registry, and that’s exactly how
+ // MacModelAndBoard() gets the data, so it wouldn’t be a very useful test.
+ std::string model;
+ std::string board;
+ MacModelAndBoard(&model, &board);
+
+ EXPECT_FALSE(model.empty());
+ EXPECT_FALSE(board.empty());
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mac/service_management.cc b/chromium/third_party/crashpad/crashpad/util/mac/service_management.cc
new file mode 100644
index 00000000000..9940006ade7
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mac/service_management.cc
@@ -0,0 +1,136 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mac/service_management.h"
+
+#include <errno.h>
+#include <launch.h>
+
+#include "base/mac/scoped_launch_data.h"
+#include "util/mac/launchd.h"
+#include "util/misc/clock.h"
+
+namespace crashpad {
+
+namespace {
+
+launch_data_t LaunchDataDictionaryForJob(const std::string& label) {
+ base::mac::ScopedLaunchData request(LaunchDataAlloc(LAUNCH_DATA_DICTIONARY));
+ LaunchDataDictInsert(
+ request, LaunchDataNewString(label.c_str()), LAUNCH_KEY_GETJOB);
+
+ base::mac::ScopedLaunchData response(LaunchMsg(request));
+ if (LaunchDataGetType(response) != LAUNCH_DATA_DICTIONARY) {
+ return nullptr;
+ }
+
+ return response.release();
+}
+
+} // namespace
+
+bool ServiceManagementSubmitJob(CFDictionaryRef job_cf) {
+ base::mac::ScopedLaunchData job_launch(CFPropertyToLaunchData(job_cf));
+ if (!job_launch.get()) {
+ return false;
+ }
+
+ base::mac::ScopedLaunchData jobs(LaunchDataAlloc(LAUNCH_DATA_ARRAY));
+ LaunchDataArraySetIndex(jobs, job_launch.release(), 0);
+
+ base::mac::ScopedLaunchData request(LaunchDataAlloc(LAUNCH_DATA_DICTIONARY));
+ LaunchDataDictInsert(request, jobs.release(), LAUNCH_KEY_SUBMITJOB);
+
+ base::mac::ScopedLaunchData response(LaunchMsg(request));
+ if (LaunchDataGetType(response) != LAUNCH_DATA_ARRAY) {
+ return false;
+ }
+
+ if (LaunchDataArrayGetCount(response) != 1) {
+ return false;
+ }
+
+ launch_data_t response_element = LaunchDataArrayGetIndex(response, 0);
+ if (LaunchDataGetType(response_element) != LAUNCH_DATA_ERRNO) {
+ return false;
+ }
+
+ int err = LaunchDataGetErrno(response_element);
+ if (err != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+bool ServiceManagementRemoveJob(const std::string& label, bool wait) {
+ base::mac::ScopedLaunchData request(LaunchDataAlloc(LAUNCH_DATA_DICTIONARY));
+ LaunchDataDictInsert(
+ request, LaunchDataNewString(label.c_str()), LAUNCH_KEY_REMOVEJOB);
+
+ base::mac::ScopedLaunchData response(LaunchMsg(request));
+ if (LaunchDataGetType(response) != LAUNCH_DATA_ERRNO) {
+ return false;
+ }
+
+ int err = LaunchDataGetErrno(response);
+ if (err == EINPROGRESS) {
+ if (wait) {
+ // TODO(mark): Use a kqueue to wait for the process to exit. To avoid a
+ // race, the kqueue would need to be set up prior to asking launchd to
+ // remove the job. Even so, the job’s PID may change between the time it’s
+ // obtained and the time the kqueue is set up, so this is nontrivial.
+ do {
+ SleepNanoseconds(1E5); // 100 microseconds
+ } while (ServiceManagementIsJobLoaded(label));
+ }
+
+ return true;
+ }
+
+ if (err != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+bool ServiceManagementIsJobLoaded(const std::string& label) {
+ base::mac::ScopedLaunchData dictionary(LaunchDataDictionaryForJob(label));
+ if (!dictionary) {
+ return false;
+ }
+
+ return true;
+}
+
+pid_t ServiceManagementIsJobRunning(const std::string& label) {
+ base::mac::ScopedLaunchData dictionary(LaunchDataDictionaryForJob(label));
+ if (!dictionary) {
+ return 0;
+ }
+
+ launch_data_t pid = LaunchDataDictLookup(dictionary, LAUNCH_JOBKEY_PID);
+ if (!pid) {
+ return 0;
+ }
+
+ if (LaunchDataGetType(pid) != LAUNCH_DATA_INTEGER) {
+ return 0;
+ }
+
+ return LaunchDataGetInteger(pid);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mac/service_management.h b/chromium/third_party/crashpad/crashpad/util/mac/service_management.h
new file mode 100644
index 00000000000..802e5449e7a
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mac/service_management.h
@@ -0,0 +1,81 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MAC_SERVICE_MANAGEMENT_H_
+#define CRASHPAD_UTIL_MAC_SERVICE_MANAGEMENT_H_
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <unistd.h>
+
+#include <string>
+
+namespace crashpad {
+
+//! \brief Submits a job to the user launchd domain as in `SMJobSubmit()`.
+//!
+//! \param[in] job_cf A dictionary describing a job.
+//!
+//! \return `true` if the job was submitted successfully, otherwise `false`.
+//!
+//! \note This function is provided because `SMJobSubmit()` is deprecated in Mac
+//! OS X 10.10. It may or may not be implemented using `SMJobSubmit()` from
+//! `ServiceManagement.framework`.
+bool ServiceManagementSubmitJob(CFDictionaryRef job_cf);
+
+//! \brief Removes a job from the user launchd domain as in `SMJobRemove()`.
+//!
+//! \param[in] label The label for the job to remove.
+//! \param[in] wait `true` if this function should block, waiting for the job to
+//! be removed. `false` if the job may be removed asynchronously.
+//!
+//! \return `true` if the job was removed successfully or if an asynchronous
+//! attempt to remove the job was started successfully, otherwise `false`.
+//!
+//! \note This function is provided because `SMJobRemove()` is deprecated in Mac
+//! OS X 10.10. On Mac OS X 10.10, observed in DP8 14A361c, it also blocks
+//! for far too long (`_block_until_job_exits()` contains a one-second
+//! `sleep()`, filed as radar 18398683) and does not signal failure via its
+//! return value when asked to remove a nonexistent job (filed as radar
+//! 18268941).
+bool ServiceManagementRemoveJob(const std::string& label, bool wait);
+
+//! \brief Determines whether a specified job is loaded in the user launchd
+//! domain.
+//!
+//! \param[in] label The label for the job to look up.
+//!
+//! \return `true` if the job is loaded, otherwise `false`.
+//!
+//! \note A loaded job is not necessarily presently running, nor has it
+//! necessarily ever run in the past.
+//! \note This function is provided because `SMJobCopyDictionary()` is
+//! deprecated in Mac OS X 10.10. It may or may not be implemented using
+//! `SMJobCopyDictionary()` from `ServiceManagement.framework`.
+bool ServiceManagementIsJobLoaded(const std::string& label);
+
+//! \brief Determines whether a specified job is running in the user launchd
+//! domain.
+//!
+//! \param[in] label The label for the job to look up.
+//!
+//! \return The job’s process ID if running, otherwise `0`.
+//!
+//! \note This function is provided because `SMJobCopyDictionary()` is
+//! deprecated in Mac OS X 10.10. It may or may not be implemented using
+//! `SMJobCopyDictionary()` from `ServiceManagement.framework`.
+pid_t ServiceManagementIsJobRunning(const std::string& label);
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MAC_SERVICE_MANAGEMENT
diff --git a/chromium/third_party/crashpad/crashpad/util/mac/service_management_test.mm b/chromium/third_party/crashpad/crashpad/util/mac/service_management_test.mm
new file mode 100644
index 00000000000..18eb9d60ca9
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mac/service_management_test.mm
@@ -0,0 +1,163 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mac/service_management.h"
+
+#import <Foundation/Foundation.h>
+#include <launch.h>
+
+#include <string>
+#include <vector>
+
+#include "base/mac/foundation_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/rand_util.h"
+#include "gtest/gtest.h"
+#include "util/misc/clock.h"
+#include "util/posix/process_info.h"
+#include "util/stdlib/objc.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+// Ensures that the process with the specified PID is running, identifying it by
+// requiring that its argv[argc - 1] compare equal to last_arg.
+void ExpectProcessIsRunning(pid_t pid, std::string& last_arg) {
+ ProcessInfo process_info;
+ ASSERT_TRUE(process_info.Initialize(pid));
+
+ // The process may not have called exec yet, so loop with a small delay while
+ // looking for the cookie.
+ int outer_tries = 10;
+ std::vector<std::string> job_argv;
+ while (outer_tries--) {
+ // If the process is in the middle of calling exec, process_info.Arguments()
+ // may fail. Loop with a small retry delay while waiting for the expected
+ // successful call.
+ int inner_tries = 10;
+ bool success;
+ do {
+ success = process_info.Arguments(&job_argv);
+ if (success) {
+ break;
+ }
+ if (inner_tries > 0) {
+ SleepNanoseconds(1E6); // 1 millisecond
+ }
+ } while (inner_tries--);
+ ASSERT_TRUE(success);
+
+ ASSERT_FALSE(job_argv.empty());
+ if (job_argv.back() == last_arg) {
+ break;
+ }
+
+ if (outer_tries > 0) {
+ SleepNanoseconds(1E6); // 1 millisecond
+ }
+ }
+
+ ASSERT_FALSE(job_argv.empty());
+ EXPECT_EQ(last_arg, job_argv.back());
+}
+
+// Ensures that the process with the specified PID is not running. Because the
+// PID may be reused for another process, a process is only treated as running
+// if its argv[argc - 1] compares equal to last_arg.
+void ExpectProcessIsNotRunning(pid_t pid, std::string& last_arg) {
+ // The process may not have exited yet, so loop with a small delay while
+ // checking that it has exited.
+ int tries = 10;
+ std::vector<std::string> job_argv;
+ while (tries--) {
+ ProcessInfo process_info;
+ if (!process_info.Initialize(pid) || !process_info.Arguments(&job_argv)) {
+ // The PID was not found.
+ return;
+ }
+
+ // The PID was found. It may have been recycled for another process. Make
+ // sure that the cookie isn’t found.
+ ASSERT_FALSE(job_argv.empty());
+ if (job_argv.back() != last_arg) {
+ break;
+ }
+
+ if (tries > 0) {
+ SleepNanoseconds(1E6); // 1 millisecond
+ }
+ }
+
+ ASSERT_FALSE(job_argv.empty());
+ EXPECT_NE(last_arg, job_argv.back());
+}
+
+TEST(ServiceManagement, SubmitRemoveJob) {
+ @autoreleasepool {
+ std::string cookie;
+ for (int index = 0; index < 16; ++index) {
+ cookie.append(1, base::RandInt('A', 'Z'));
+ }
+
+ std::string shell_script =
+ base::StringPrintf("sleep 10; echo %s", cookie.c_str());
+ NSString* shell_script_ns = base::SysUTF8ToNSString(shell_script);
+
+ const char kJobLabel[] = "com.googlecode.crashpad.test.service_management";
+ NSDictionary* job_dictionary_ns = @{
+ @LAUNCH_JOBKEY_LABEL : @"com.googlecode.crashpad.test.service_management",
+ @LAUNCH_JOBKEY_RUNATLOAD : @YES,
+ @LAUNCH_JOBKEY_PROGRAMARGUMENTS :
+ @[ @"/bin/sh", @"-c", shell_script_ns, ],
+ };
+ CFDictionaryRef job_dictionary_cf =
+ base::mac::NSToCFCast(job_dictionary_ns);
+
+ // The job may be left over from a failed previous run.
+ if (ServiceManagementIsJobLoaded(kJobLabel)) {
+ EXPECT_TRUE(ServiceManagementRemoveJob(kJobLabel, true));
+ }
+
+ EXPECT_FALSE(ServiceManagementIsJobLoaded(kJobLabel));
+ ASSERT_FALSE(ServiceManagementIsJobRunning(kJobLabel));
+
+ // Submit the job.
+ ASSERT_TRUE(ServiceManagementSubmitJob(job_dictionary_cf));
+ EXPECT_TRUE(ServiceManagementIsJobLoaded(kJobLabel));
+
+ // launchd started the job because RunAtLoad is true.
+ pid_t job_pid = ServiceManagementIsJobRunning(kJobLabel);
+ ASSERT_GT(job_pid, 0);
+
+ ExpectProcessIsRunning(job_pid, shell_script);
+
+ // Remove the job.
+ ASSERT_TRUE(ServiceManagementRemoveJob(kJobLabel, true));
+ EXPECT_FALSE(ServiceManagementIsJobLoaded(kJobLabel));
+ EXPECT_EQ(0, ServiceManagementIsJobRunning(kJobLabel));
+
+ // Now that the job is unloaded, a subsequent attempt to unload it should be
+ // an error.
+ EXPECT_FALSE(ServiceManagementRemoveJob(kJobLabel, false));
+
+ ExpectProcessIsNotRunning(job_pid, shell_script);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mac/xattr.cc b/chromium/third_party/crashpad/crashpad/util/mac/xattr.cc
new file mode 100644
index 00000000000..54a8a09aac2
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mac/xattr.cc
@@ -0,0 +1,148 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mac/xattr.h"
+
+#include <errno.h>
+#include <stdint.h>
+#include <sys/xattr.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/string_number_conversions.h"
+
+namespace crashpad {
+
+XattrStatus ReadXattr(const base::FilePath& file,
+ const base::StringPiece& name,
+ std::string* value) {
+ // First get the size of the attribute value.
+ ssize_t buffer_size = getxattr(file.value().c_str(), name.data(), nullptr,
+ 0, 0, 0);
+ if (buffer_size < 0) {
+ if (errno == ENOATTR)
+ return XattrStatus::kNoAttribute;
+ PLOG(ERROR) << "getxattr size " << name << " on file " << file.value();
+ return XattrStatus::kOtherError;
+ }
+
+ // Resize the buffer and read into it.
+ value->resize(buffer_size);
+ ssize_t bytes_read = getxattr(file.value().c_str(), name.data(),
+ &(*value)[0], value->size(),
+ 0, 0);
+ if (bytes_read < 0) {
+ PLOG(ERROR) << "getxattr " << name << " on file " << file.value();
+ return XattrStatus::kOtherError;
+ }
+ DCHECK_EQ(bytes_read, buffer_size);
+
+ return XattrStatus::kOK;
+}
+
+bool WriteXattr(const base::FilePath& file,
+ const base::StringPiece& name,
+ const std::string& value) {
+ int rv = setxattr(file.value().c_str(), name.data(), value.c_str(),
+ value.length(), 0, 0);
+ PLOG_IF(ERROR, rv != 0) << "setxattr " << name << " on file "
+ << file.value();
+ return rv == 0;
+}
+
+XattrStatus ReadXattrBool(const base::FilePath& file,
+ const base::StringPiece& name,
+ bool* value) {
+ std::string tmp;
+ XattrStatus status;
+ if ((status = ReadXattr(file, name, &tmp)) != XattrStatus::kOK)
+ return status;
+ if (tmp == "1") {
+ *value = true;
+ return XattrStatus::kOK;
+ } else if (tmp == "0") {
+ *value = false;
+ return XattrStatus::kOK;
+ } else {
+ LOG(ERROR) << "ReadXattrBool " << name << " on file " << file.value()
+ << " could not be interpreted as boolean";
+ return XattrStatus::kOtherError;
+ }
+}
+
+bool WriteXattrBool(const base::FilePath& file,
+ const base::StringPiece& name,
+ bool value) {
+ return WriteXattr(file, name, (value ? "1" : "0"));
+}
+
+XattrStatus ReadXattrInt(const base::FilePath& file,
+ const base::StringPiece& name,
+ int* value) {
+ std::string tmp;
+ XattrStatus status;
+ if ((status = ReadXattr(file, name, &tmp)) != XattrStatus::kOK)
+ return status;
+ if (!base::StringToInt(tmp, value)) {
+ LOG(ERROR) << "ReadXattrInt " << name << " on file " << file.value()
+ << " could not be converted to an int";
+ return XattrStatus::kOtherError;
+ }
+ return XattrStatus::kOK;
+}
+
+bool WriteXattrInt(const base::FilePath& file,
+ const base::StringPiece& name,
+ int value) {
+ std::string tmp = base::StringPrintf("%d", value);
+ return WriteXattr(file, name, tmp);
+}
+
+XattrStatus ReadXattrTimeT(const base::FilePath& file,
+ const base::StringPiece& name,
+ time_t* value) {
+ // time_t on OS X is defined as a long, but it will be read into an
+ // int64_t here, since there is no string conversion method for long.
+ std::string tmp;
+ XattrStatus status;
+ if ((status = ReadXattr(file, name, &tmp)) != XattrStatus::kOK)
+ return status;
+
+ int64_t encoded_value;
+ if (!base::StringToInt64(tmp, &encoded_value)) {
+ LOG(ERROR) << "ReadXattrTimeT " << name << " on file " << file.value()
+ << " could not be converted to an int";
+ return XattrStatus::kOtherError;
+ }
+
+ *value = base::saturated_cast<time_t>(encoded_value);
+ if (!base::IsValueInRangeForNumericType<time_t>(encoded_value)) {
+ LOG(ERROR) << "ReadXattrTimeT " << name << " on file " << file.value()
+ << " read over-sized value and will saturate";
+ return XattrStatus::kOtherError;
+ }
+
+ return XattrStatus::kOK;
+}
+
+bool WriteXattrTimeT(const base::FilePath& file,
+ const base::StringPiece& name,
+ time_t value) {
+ std::string tmp = base::StringPrintf("%ld", value);
+ return WriteXattr(file, name, tmp);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mac/xattr.h b/chromium/third_party/crashpad/crashpad/util/mac/xattr.h
new file mode 100644
index 00000000000..3e14f672c9c
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mac/xattr.h
@@ -0,0 +1,97 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MAC_XATTR_H_
+#define CRASHPAD_UTIL_MAC_XATTR_H_
+
+#include <time.h>
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/strings/string_piece.h"
+
+namespace crashpad {
+
+//! \brief The result code for a ReadXattr operation.
+enum class XattrStatus {
+ //! \brief No error occured. No message is logged.
+ kOK = 0,
+
+ //! \brief The attribute does not exist. No message is logged.
+ kNoAttribute,
+
+ //! \brief An error occurred and an error message was logged.
+ kOtherError,
+};
+
+//! \brief Reads an extended attribute on a file.
+//!
+//! \param[in] file The path to the file.
+//! \param[in] name The name of the extended attribute to read.
+//! \param[out] value The value of the attribute.
+//!
+//! \return XattrStatus
+XattrStatus ReadXattr(const base::FilePath& file,
+ const base::StringPiece& name,
+ std::string* value);
+
+//! \brief Writes an extended attribute on a file.
+//!
+//! \param[in] file The path to the file.
+//! \param[in] name The name of the extended attribute to write.
+//! \param[in] value The value of the attribute.
+//!
+//! \return `true` if the write was successful. `false` on error, with a message
+//! logged.
+bool WriteXattr(const base::FilePath& file,
+ const base::StringPiece& name,
+ const std::string& value);
+
+//! \copydoc ReadXattr
+//!
+//! Only the values `"0"` and `"1"`, for `false` and `true` respectively, are
+//! valid conversions.
+XattrStatus ReadXattrBool(const base::FilePath& file,
+ const base::StringPiece& name,
+ bool* value);
+
+//! \copydoc WriteXattr
+bool WriteXattrBool(const base::FilePath& file,
+ const base::StringPiece& name,
+ bool value);
+
+//! \copydoc ReadXattr
+XattrStatus ReadXattrInt(const base::FilePath& file,
+ const base::StringPiece& name,
+ int* value);
+
+//! \copydoc WriteXattr
+bool WriteXattrInt(const base::FilePath& file,
+ const base::StringPiece& name,
+ int value);
+
+//! \copydoc ReadXattr
+XattrStatus ReadXattrTimeT(const base::FilePath& file,
+ const base::StringPiece& name,
+ time_t* value);
+
+//! \copydoc WriteXattr
+bool WriteXattrTimeT(const base::FilePath& file,
+ const base::StringPiece& name,
+ time_t value);
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MAC_XATTR_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/mac/xattr_test.cc b/chromium/third_party/crashpad/crashpad/util/mac/xattr_test.cc
new file mode 100644
index 00000000000..a156a0a7c16
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mac/xattr_test.cc
@@ -0,0 +1,122 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mac/xattr.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <limits>
+
+#include "base/files/scoped_file.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+#include "test/errors.h"
+#include "test/scoped_temp_dir.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+class Xattr : public testing::Test {
+ protected:
+ // testing::Test:
+
+ void SetUp() override {
+ path_ = temp_dir_.path().Append("xattr_file");
+ base::ScopedFD tmp(HANDLE_EINTR(
+ open(path_.value().c_str(), O_CREAT | O_TRUNC, 0644)));
+ EXPECT_GE(tmp.get(), 0) << ErrnoMessage("open");
+ }
+
+ void TearDown() override {
+ EXPECT_EQ(0, unlink(path_.value().c_str())) << ErrnoMessage("unlink");
+ }
+
+ const base::FilePath& path() const { return path_; }
+
+ private:
+ ScopedTempDir temp_dir_;
+ base::FilePath path_;
+};
+
+const char kKey[] = "com.google.crashpad.test";
+
+TEST_F(Xattr, ReadNonExistentXattr) {
+ std::string value;
+ EXPECT_EQ(XattrStatus::kNoAttribute, ReadXattr(path(), kKey, &value));
+}
+
+TEST_F(Xattr, WriteAndReadString) {
+ std::string value = "hello world";
+ EXPECT_TRUE(WriteXattr(path(), kKey, value));
+
+ std::string actual;
+ EXPECT_EQ(XattrStatus::kOK, ReadXattr(path(), kKey, &actual));
+ EXPECT_EQ(value, actual);
+}
+
+TEST_F(Xattr, WriteAndReadVeryLongString) {
+ std::string value(533, 'A');
+ EXPECT_TRUE(WriteXattr(path(), kKey, value));
+
+ std::string actual;
+ EXPECT_EQ(XattrStatus::kOK, ReadXattr(path(), kKey, &actual));
+ EXPECT_EQ(value, actual);
+}
+
+TEST_F(Xattr, WriteAndReadBool) {
+ EXPECT_TRUE(WriteXattrBool(path(), kKey, true));
+ bool actual = false;
+ EXPECT_EQ(XattrStatus::kOK, ReadXattrBool(path(), kKey, &actual));
+ EXPECT_TRUE(actual);
+
+ EXPECT_TRUE(WriteXattrBool(path(), kKey, false));
+ EXPECT_EQ(XattrStatus::kOK, ReadXattrBool(path(), kKey, &actual));
+ EXPECT_FALSE(actual);
+}
+
+TEST_F(Xattr, WriteAndReadInt) {
+ int expected = 42;
+ int actual;
+
+ EXPECT_TRUE(WriteXattrInt(path(), kKey, expected));
+ EXPECT_EQ(XattrStatus::kOK, ReadXattrInt(path(), kKey, &actual));
+ EXPECT_EQ(expected, actual);
+
+ expected = std::numeric_limits<int>::max();
+ EXPECT_TRUE(WriteXattrInt(path(), kKey, expected));
+ EXPECT_EQ(XattrStatus::kOK, ReadXattrInt(path(), kKey, &actual));
+ EXPECT_EQ(expected, actual);
+}
+
+TEST_F(Xattr, WriteAndReadTimeT) {
+ time_t expected = time(nullptr);
+ time_t actual;
+
+ EXPECT_TRUE(WriteXattrTimeT(path(), kKey, expected));
+ EXPECT_EQ(XattrStatus::kOK, ReadXattrTimeT(path(), kKey, &actual));
+ EXPECT_EQ(expected, actual);
+
+ expected = std::numeric_limits<time_t>::max();
+ EXPECT_TRUE(WriteXattrTimeT(path(), kKey, expected));
+ EXPECT_EQ(XattrStatus::kOK, ReadXattrTimeT(path(), kKey, &actual));
+ EXPECT_EQ(expected, actual);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/child_port.defs b/chromium/third_party/crashpad/crashpad/util/mach/child_port.defs
new file mode 100644
index 00000000000..ce88c115c45
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/child_port.defs
@@ -0,0 +1,64 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <mach/mach_types.defs>
+#include <mach/std_types.defs>
+
+// child_port provides an interface for port rights to be transferred between
+// tasks. Its expected usage is for child processes to be able to pass port
+// rights to their parent processes. A child may wish to give its parent a copy
+// of a send right to its own task port, or a child may hold a receive right for
+// a server and wish to furnish its parent with a send right to that server.
+//
+// This Mach subsystem defines the lowest-level interface for these rights to
+// be transferred. Most users will not user this interface directly, but will
+// use ChildPortHandshake, which builds on this interface by providing client
+// and server implementations, along with a protocol for establishing
+// communication in a parent-child process relationship.
+subsystem child_port 10011;
+
+serverprefix handle_;
+
+type child_port_server_t = mach_port_t;
+type child_port_token_t = uint64_t;
+
+import "util/mach/child_port_types.h";
+
+// Sends a Mach port right across an IPC boundary.
+//
+// server[in]: The server to send the port right to.
+// token[in]: A random opaque token, generated by the server and communicated to
+// the client through some secure means such as a shared pipe. The client
+// includes the token in its request to prove its authenticity to the
+// server. This parameter is necessary for instances where the server must
+// publish its service broadly, such as via the bootstrap server. When this
+// is done, anyone with access to the bootstrap server will be able to gain
+// rights to communicate with |server|, and |token| serves as a shared
+// secret allowing the server to verify that it has received a request from
+// the intended client. |server| will reject requests with an invalid
+// |token|.
+// port[in]: A port right to transfer to the server. In expected usage, this may
+// be a send or send-once right, and the |server| will reject a receive
+// right. It is permissible to specify make-send for a receive right.
+//
+// Return value: As this is a “simpleroutine”, the server does not respond to
+// the client request, and the client does not block waiting for a response
+// after sending its request. The return value is MACH_MSG_SUCCESS if the
+// request was queued for the server, without any indication of whether the
+// server considered the request valid or took any action. On data
+// validation or mach_msg() failure, another code will be returned
+// indicating the nature of the error.
+simpleroutine child_port_check_in(server: child_port_server_t;
+ token: child_port_token_t;
+ port: mach_port_poly_t);
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/child_port_handshake.cc b/chromium/third_party/crashpad/crashpad/util/mach/child_port_handshake.cc
new file mode 100644
index 00000000000..eedcf47b267
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/child_port_handshake.cc
@@ -0,0 +1,347 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/child_port_handshake.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <servers/bootstrap.h>
+#include <sys/event.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/rand_util.h"
+#include "base/strings/stringprintf.h"
+#include "util/file/file_io.h"
+#include "util/mach/child_port.h"
+#include "util/mach/mach_extensions.h"
+#include "util/mach/mach_message.h"
+#include "util/mach/mach_message_server.h"
+
+namespace crashpad {
+
+ChildPortHandshake::ChildPortHandshake()
+ : token_(0),
+ pipe_read_(),
+ pipe_write_(),
+ child_port_(MACH_PORT_NULL),
+ checked_in_(false) {
+ // Use socketpair() instead of pipe(). There is no way to suppress SIGPIPE on
+ // pipes in Mac OS X 10.6, because the F_SETNOSIGPIPE fcntl() command was not
+ // introduced until 10.7.
+ int pipe_fds[2];
+ PCHECK(socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_fds) == 0)
+ << "socketpair";
+
+ pipe_read_.reset(pipe_fds[0]);
+ pipe_write_.reset(pipe_fds[1]);
+
+ // Simulate pipe() semantics by shutting down the “wrong” sides of the socket.
+ PCHECK(shutdown(pipe_write_.get(), SHUT_RD) == 0) << "shutdown";
+ PCHECK(shutdown(pipe_read_.get(), SHUT_WR) == 0) << "shutdown";
+
+ // SIGPIPE is undesirable when writing to this pipe. Allow broken-pipe writes
+ // to fail with EPIPE instead.
+ const int value = 1;
+ PCHECK(setsockopt(
+ pipe_write_.get(), SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value)) == 0)
+ << "setsockopt";
+}
+
+ChildPortHandshake::~ChildPortHandshake() {
+}
+
+int ChildPortHandshake::ReadPipeFD() const {
+ DCHECK_NE(pipe_read_.get(), -1);
+ return pipe_read_.get();
+}
+
+mach_port_t ChildPortHandshake::RunServer() {
+ DCHECK_NE(pipe_read_.get(), -1);
+ pipe_read_.reset();
+
+ // Transfer ownership of the write pipe into this method’s scope.
+ base::ScopedFD pipe_write_owner = pipe_write_.Pass();
+
+ // Initialize the token and share it with the client via the pipe.
+ token_ = base::RandUint64();
+ int pipe_write = pipe_write_owner.get();
+ if (!LoggingWriteFile(pipe_write, &token_, sizeof(token_))) {
+ LOG(WARNING) << "no client check-in";
+ return MACH_PORT_NULL;
+ }
+
+ // Create a unique name for the bootstrap service mapping. Make it unguessable
+ // to prevent outsiders from grabbing the name first, which would cause
+ // bootstrap_check_in() to fail.
+ uint64_t thread_id;
+ errno = pthread_threadid_np(pthread_self(), &thread_id);
+ PCHECK(errno == 0) << "pthread_threadid_np";
+ std::string service_name = base::StringPrintf(
+ "com.googlecode.crashpad.child_port_handshake.%d.%llu.%016llx",
+ getpid(),
+ thread_id,
+ base::RandUint64());
+ DCHECK_LT(service_name.size(), implicit_cast<size_t>(BOOTSTRAP_MAX_NAME_LEN));
+
+ // Check the new service in with the bootstrap server, obtaining a receive
+ // right for it.
+ mach_port_t server_port;
+ kern_return_t kr =
+ bootstrap_check_in(bootstrap_port, service_name.c_str(), &server_port);
+ BOOTSTRAP_CHECK(kr == BOOTSTRAP_SUCCESS, kr) << "bootstrap_check_in";
+ base::mac::ScopedMachReceiveRight server_port_owner(server_port);
+
+ // Share the service name with the client via the pipe.
+ uint32_t service_name_length = service_name.size();
+ if (!LoggingWriteFile(
+ pipe_write, &service_name_length, sizeof(service_name_length))) {
+ LOG(WARNING) << "no client check-in";
+ return MACH_PORT_NULL;
+ }
+
+ if (!LoggingWriteFile(
+ pipe_write, service_name.c_str(), service_name_length)) {
+ LOG(WARNING) << "no client check-in";
+ return MACH_PORT_NULL;
+ }
+
+ // A kqueue cannot monitor a raw Mach receive right with EVFILT_MACHPORT. It
+ // requires a port set. Create a new port set and add the receive right to it.
+ base::mac::ScopedMachPortSet server_port_set(
+ NewMachPort(MACH_PORT_RIGHT_PORT_SET));
+ CHECK_NE(server_port_set, kMachPortNull);
+
+ kr = mach_port_insert_member(mach_task_self(), server_port, server_port_set);
+ MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member";
+
+ // Set up a kqueue to monitor both the server’s receive right and the write
+ // side of the pipe. Messages from the client will be received via the receive
+ // right, and the pipe will show EOF if the client closes its read side
+ // prematurely.
+ base::ScopedFD kq(kqueue());
+ PCHECK(kq != -1) << "kqueue";
+
+ struct kevent changelist[2];
+ EV_SET(&changelist[0],
+ server_port_set,
+ EVFILT_MACHPORT,
+ EV_ADD | EV_CLEAR,
+ 0,
+ 0,
+ nullptr);
+ EV_SET(&changelist[1],
+ pipe_write,
+ EVFILT_WRITE,
+ EV_ADD | EV_CLEAR,
+ 0,
+ 0,
+ nullptr);
+ int rv = HANDLE_EINTR(
+ kevent(kq.get(), changelist, arraysize(changelist), nullptr, 0, nullptr));
+ PCHECK(rv != -1) << "kevent";
+
+ ChildPortServer child_port_server(this);
+
+ bool blocking = true;
+ DCHECK(!checked_in_);
+ while (!checked_in_) {
+ DCHECK_EQ(child_port_, kMachPortNull);
+
+ // Get a kevent from the kqueue. Block while waiting for an event unless the
+ // write pipe has arrived at EOF, in which case the kevent() should be
+ // nonblocking. Although the client sends its check-in message before
+ // closing the read side of the pipe, this organization allows the events to
+ // be delivered out of order and the check-in message will still be
+ // processed.
+ struct kevent event;
+ const timespec nonblocking_timeout = {};
+ const timespec* timeout = blocking ? nullptr : &nonblocking_timeout;
+ rv = HANDLE_EINTR(kevent(kq.get(), nullptr, 0, &event, 1, timeout));
+ PCHECK(rv != -1) << "kevent";
+
+ if (rv == 0) {
+ // Non-blocking kevent() with no events to return.
+ DCHECK(!blocking);
+ LOG(WARNING) << "no client check-in";
+ return MACH_PORT_NULL;
+ }
+
+ DCHECK_EQ(rv, 1);
+
+ if (event.flags & EV_ERROR) {
+ // kevent() may have put its error here.
+ errno = event.data;
+ PLOG(FATAL) << "kevent";
+ }
+
+ switch (event.filter) {
+ case EVFILT_MACHPORT: {
+ // There’s something to receive on the port set.
+ DCHECK_EQ(event.ident, server_port_set);
+
+ // Run the message server in an inner loop instead of using
+ // MachMessageServer::kPersistent. This allows the loop to exit as soon
+ // as child_port_ is set, even if other messages are queued. This needs
+ // to drain all messages, because the use of edge triggering (EV_CLEAR)
+ // means that if more than one message is in the queue when kevent()
+ // returns, no more notifications will be generated.
+ while (!checked_in_) {
+ // If a proper message is received from child_port_check_in(),
+ // this will call HandleChildPortCheckIn().
+ mach_msg_return_t mr =
+ MachMessageServer::Run(&child_port_server,
+ server_port_set,
+ MACH_MSG_OPTION_NONE,
+ MachMessageServer::kOneShot,
+ MachMessageServer::kReceiveLargeIgnore,
+ kMachMessageTimeoutNonblocking);
+ if (mr == MACH_RCV_TIMED_OUT) {
+ break;
+ } else if (mr != MACH_MSG_SUCCESS) {
+ MACH_LOG(ERROR, mr) << "MachMessageServer::Run";
+ return MACH_PORT_NULL;
+ }
+ }
+ break;
+ }
+
+ case EVFILT_WRITE:
+ // The write pipe is ready to be written to, or it’s at EOF. The former
+ // case is uninteresting, but a notification for this may be presented
+ // because the write pipe will be ready to be written to, at the latest,
+ // when the client reads its messages from the read side of the same
+ // pipe. Ignore that case. Multiple notifications for that situation
+ // will not be generated because edge triggering (EV_CLEAR) is used
+ // above.
+ DCHECK_EQ(implicit_cast<int>(event.ident), pipe_write);
+ if (event.flags & EV_EOF) {
+ // There are no readers attached to the write pipe. The client has
+ // closed its side of the pipe. There can be one last shot at
+ // receiving messages, in case the check-in message is delivered
+ // out of order, after the EOF notification.
+ blocking = false;
+ }
+ break;
+
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ mach_port_t child_port = MACH_PORT_NULL;
+ std::swap(child_port_, child_port);
+ return child_port;
+}
+
+kern_return_t ChildPortHandshake::HandleChildPortCheckIn(
+ child_port_server_t server,
+ const child_port_token_t token,
+ mach_port_t port,
+ mach_msg_type_name_t right_type,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_request) {
+ DCHECK_EQ(child_port_, kMachPortNull);
+
+ if (token != token_) {
+ // If the token’s not correct, someone’s attempting to spoof the legitimate
+ // client.
+ LOG(WARNING) << "ignoring incorrect token";
+ *destroy_request = true;
+ } else {
+ checked_in_ = true;
+
+ if (right_type == MACH_MSG_TYPE_PORT_RECEIVE) {
+ // The message needs to carry a send right or a send-once right. This
+ // isn’t a strict requirement of the protocol, but users of this class
+ // expect a send right or a send-once right, both of which can be managed
+ // by base::mac::ScopedMachSendRight. It is invalid to store a receive
+ // right in that scoper.
+ LOG(WARNING) << "ignoring MACH_MSG_TYPE_PORT_RECEIVE";
+ *destroy_request = true;
+ } else {
+ // Communicate the child port back to the RunServer().
+ // *destroy_request is left at false, because RunServer() needs the right
+ // to remain intact. It gives ownership of the right to its caller.
+ child_port_ = port;
+ }
+ }
+
+ // This is a MIG simpleroutine, there is no reply message.
+ return MIG_NO_REPLY;
+}
+
+// static
+void ChildPortHandshake::RunClient(int pipe_read,
+ mach_port_t port,
+ mach_msg_type_name_t right_type) {
+ base::ScopedFD pipe_read_owner(pipe_read);
+
+ // Read the token and the service name from the read side of the pipe.
+ child_port_token_t token;
+ std::string service_name;
+ RunClientInternal_ReadPipe(pipe_read, &token, &service_name);
+
+ // Look up the server and check in with it by providing the token and port.
+ RunClientInternal_SendCheckIn(service_name, token, port, right_type);
+}
+
+// static
+void ChildPortHandshake::RunClientInternal_ReadPipe(int pipe_read,
+ child_port_token_t* token,
+ std::string* service_name) {
+ // Read the token from the pipe.
+ CheckedReadFile(pipe_read, token, sizeof(*token));
+
+ // Read the service name from the pipe.
+ uint32_t service_name_length;
+ CheckedReadFile(pipe_read, &service_name_length, sizeof(service_name_length));
+ DCHECK_LT(service_name_length,
+ implicit_cast<uint32_t>(BOOTSTRAP_MAX_NAME_LEN));
+
+ if (service_name_length > 0) {
+ service_name->resize(service_name_length);
+ CheckedReadFile(pipe_read, &(*service_name)[0], service_name_length);
+ }
+}
+
+// static
+void ChildPortHandshake::RunClientInternal_SendCheckIn(
+ const std::string& service_name,
+ child_port_token_t token,
+ mach_port_t port,
+ mach_msg_type_name_t right_type) {
+ // Get a send right to the server by looking up the service with the bootstrap
+ // server by name.
+ mach_port_t server_port;
+ kern_return_t kr =
+ bootstrap_look_up(bootstrap_port, service_name.c_str(), &server_port);
+ BOOTSTRAP_CHECK(kr == BOOTSTRAP_SUCCESS, kr) << "bootstrap_look_up";
+ base::mac::ScopedMachSendRight server_port_owner(server_port);
+
+ // Check in with the server.
+ kr = child_port_check_in(server_port, token, port, right_type);
+ MACH_CHECK(kr == KERN_SUCCESS, kr) << "child_port_check_in";
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/child_port_handshake.h b/chromium/third_party/crashpad/crashpad/util/mach/child_port_handshake.h
new file mode 100644
index 00000000000..734220beffd
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/child_port_handshake.h
@@ -0,0 +1,227 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MACH_CHILD_PORT_HANDSHAKE_H_
+#define CRASHPAD_UTIL_MACH_CHILD_PORT_HANDSHAKE_H_
+
+#include <mach/mach.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/files/scoped_file.h"
+#include "util/mach/child_port_server.h"
+
+namespace crashpad {
+
+namespace test {
+namespace {
+class ChildPortHandshakeTest;
+} // namespace
+} // namespace test
+
+//! \brief Implements a handshake protocol that allows a parent process to
+//! obtain a Mach port right from a child process.
+//!
+//! Ordinarily, there is no way for parent and child processes to exchange port
+//! rights, outside of the rights that children inherit from their parents.
+//! These include task-special ports and exception ports, but all of these have
+//! system-defined uses, and cannot reliably be replaced: in a multi-threaded
+//! parent, it is impossible to temporarily change one an inheritable port while
+//! maintaining a guarantee that another thread will not attempt to use it, and
+//! in children, it difficult to guarantee that nothing will attempt to use an
+//! inheritable port before it can be replaced with the correct one. This latter
+//! concern is becoming increasingly more pronounced as system libraries perform
+//! more operations that rely on an inheritable port in module initializers.
+//!
+//! The protocol implemented by this class involves a server that runs in the
+//! parent process. The server is published with the bootstrap server, which the
+//! child has access to because the bootstrap port is one of the inherited
+//! task-special ports. The parent and child also share a pipe, which the parent
+//! can write to and the child can read from. After launching a child process,
+//! the parent will write a random token to this pipe, along with the name under
+//! which its server has been registered with the bootstrap server. The child
+//! can then obtain a send right to this server with `bootstrap_look_up()`, and
+//! send a check-in message containing the token value and the port right of its
+//! choice by calling `child_port_check_in()`.
+//!
+//! The inclusion of the token authenticates the child to its parent. This is
+//! necessary because the service is published with the bootstrap server, which
+//! opens up access to it to more than the child process. Because the token is
+//! passed to the child by a shared pipe, it constitutes a shared secret not
+//! known by other processes that may have incidental access to the server. The
+//! ChildPortHandshake server considers its randomly-generated token valid until
+//! a client checks in with it. This mechanism is used instead of examining the
+//! request message’s audit trailer to verify the sender’s process ID because in
+//! some process architectures, it may be impossible to verify the child’s
+//! process ID. This may happen when the child disassociates from the parent
+//! with a double fork(), and the actual client is the parent’s grandchild. In
+//! this case, the child would not check in, but the grandchild, in possession
+//! of the token, would check in.
+//!
+//! The shared pipe serves another purpose: the server monitors it for an
+//! end-of-file (no readers) condition. Once detected, it will stop its blocking
+//! wait for a client to check in. This mechanism was chosen over monitoring a
+//! child process directly for exit to account for the possibility that the
+//! child might disassociate with a double fork().
+//!
+//! This class can be used to allow a child process to provide its parent with
+//! a send right to its task port, in cases where it is desirable for the parent
+//! to have such access. It can also be used to allow a child process to
+//! establish its own server and provide its parent with a send right to that
+//! server, for cases where a service is provided and it is undesirable or
+//! impossible to provide it via the bootstrap or launchd interfaces.
+class ChildPortHandshake : public ChildPortServer::Interface {
+ public:
+ //! \brief Initializes the server.
+ //!
+ //! This creates the pipe so that the “read” side can be obtained by calling
+ //! ReadPipeFD().
+ ChildPortHandshake();
+
+ ~ChildPortHandshake();
+
+ //! \brief Obtains the “read” side of the pipe, to be used by the client.
+ //!
+ //! Callers must obtain this file descriptor and arrange for the caller to
+ //! have access to it before calling RunServer().
+ //!
+ //! \return The file descriptor that the client should read from.
+ int ReadPipeFD() const;
+
+ //! \brief Runs the server.
+ //!
+ //! This method performs these tasks:
+ //! - Closes the “read” side of the pipe in-process, so that the client
+ //! process holds the only file descriptor that can read from the pipe.
+ //! - Creates a random token and sends it via the pipe.
+ //! - Checks its service in with the bootstrap server, and sends the name
+ //! of its bootstrap service mapping via the pipe.
+ //! - Simultaneously receives messages on its Mach server and monitors the
+ //! pipe for end-of-file. This is a blocking operation.
+ //! - When a Mach message is received, calls HandleChildPortCheckIn() to
+ //! interpret and validate it, and if the message is valid, returns the
+ //! port right extracted from the message. If the message is not valid,
+ //! this method will continue waiting for a valid message. Valid messages
+ //! are properly formatted and have the correct token. If a valid message
+ //! carries a send or send-once right, it will be returned. If a valid
+ //! message contains a receive right, it will be destroyed and
+ //! `MACH_PORT_NULL` will be returned. If a message is not valid, this
+ //! method will continue waiting for pipe EOF or a valid message.
+ //! - When notified of pipe EOF, returns `MACH_PORT_NULL`.
+ //! - Regardless of return value, destroys the server’s receive right and
+ //! closes the pipe.
+ //!
+ //! \return On success, the send or send-once right to the port provided by
+ //! the client. The caller takes ownership of this right. On failure,
+ //! `MACH_PORT_NULL`, indicating that the client did not check in properly
+ //! before terminating, where termination is detected by noticing that the
+ //! read side of the shared pipe has closed. On failure, a message
+ //! indiciating the nature of the failure will be logged.
+ mach_port_t RunServer();
+
+ // ChildPortServer::Interface:
+ kern_return_t HandleChildPortCheckIn(child_port_server_t server,
+ child_port_token_t token,
+ mach_port_t port,
+ mach_msg_type_name_t right_type,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_request) override;
+
+ //! \brief Runs the client.
+ //!
+ //! This function performs these tasks:
+ //! - Reads the token from the pipe.
+ //! - Reads the bootstrap service name from the pipe.
+ //! - Obtains a send right to the server by calling `bootstrap_look_up()`.
+ //! - Sends a check-in message to the server by calling
+ //! `child_port_check_in()`, providing the token and the user-supplied port
+ //! right.
+ //! - Deallocates the send right to the server, and closes the pipe.
+ //!
+ //! There is no return value because `child_port_check_in()` is a MIG
+ //! `simpleroutine`, and the server does not send a reply. This allows
+ //! check-in to occur without blocking to wait for a reply.
+ //!
+ //! \param[in] pipe_read The “read” side of the pipe shared with the server
+ //! process.
+ //! \param[in] port The port that will be passed to the server by
+ //! `child_port_check_in()`.
+ //! \param[in] right_type The right type to furnish the parent with. If \a
+ //! port is a send right, this can be `MACH_MSG_TYPE_COPY_SEND` or
+ //! `MACH_MSG_TYPE_MOVE_SEND`. If \a port is a send-once right, this can
+ //! be `MACH_MSG_TYPE_MOVE_SEND_ONCE`. If \a port is a receive right,
+ //! this can be `MACH_MSG_TYPE_MAKE_SEND`. `MACH_MSG_TYPE_MOVE_RECEIVE`
+ //! is supported by the client interface but will be silently rejected by
+ //! server run by RunServer(), which expects to receive only send or
+ //! send-once rights.
+ static void RunClient(int pipe_read,
+ mach_port_t port,
+ mach_msg_type_name_t right_type);
+
+ private:
+ //! \brief Runs the read-from-pipe portion of the client’s side of the
+ //! handshake. This is an implementation detail of RunClient and is only
+ //! exposed for testing purposes.
+ //!
+ //! \param[in] pipe_read The “read” side of the pipe shared with the server
+ //! process.
+ //! \param[out] token The token value read from \a pipe_read.
+ //! \param[out] service_name The service name as registered with the bootstrap
+ //! server, read from \a pipe_read.
+ static void RunClientInternal_ReadPipe(int pipe_read,
+ child_port_token_t* token,
+ std::string* service_name);
+
+ //! \brief Runs the check-in portion of the client’s side of the handshake.
+ //! This is an implementation detail of RunClient and is only exposed for
+ //! testing purposes.
+ //!
+ //! \param[in] service_name The service name as registered with the bootstrap
+ //! server, to be looked up with `bootstrap_look_up()`.
+ //! \param[in] token The token value to provide during check-in.
+ //! \param[in] port The port that will be passed to the server by
+ //! `child_port_check_in()`.
+ //! \param[in] right_type The right type to furnish the parent with.
+ static void RunClientInternal_SendCheckIn(const std::string& service_name,
+ child_port_token_t token,
+ mach_port_t port,
+ mach_msg_type_name_t right_type);
+
+ // Communicates the token from RunServer(), where it’s generated, to
+ // HandleChildPortCheckIn(), where it’s validated.
+ child_port_token_t token_;
+
+ base::ScopedFD pipe_read_;
+ base::ScopedFD pipe_write_;
+
+ // Communicates the port received from the client from
+ // HandleChildPortCheckIn(), where it’s received, to RunServer(), where it’s
+ // returned. This is strongly-owned, but ownership is transferred to
+ // RunServer()’s caller.
+ mach_port_t child_port_;
+
+ // Communicates that a check-in with a valid token was received by
+ // HandleChildPortCheckIn(), and that the value of child_port_ should be
+ // returned to RunServer()’s caller.
+ bool checked_in_;
+
+ friend class test::ChildPortHandshakeTest;
+
+ DISALLOW_COPY_AND_ASSIGN(ChildPortHandshake);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MACH_CHILD_PORT_HANDSHAKE_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/child_port_handshake_test.cc b/chromium/third_party/crashpad/crashpad/util/mach/child_port_handshake_test.cc
new file mode 100644
index 00000000000..12ca424c54c
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/child_port_handshake_test.cc
@@ -0,0 +1,188 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/child_port_handshake.h"
+
+#include "base/mac/scoped_mach_port.h"
+#include "gtest/gtest.h"
+#include "test/multiprocess.h"
+#include "util/mach/child_port_types.h"
+#include "util/mach/mach_extensions.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+class ChildPortHandshakeTest : public Multiprocess {
+ public:
+ enum TestType {
+ kTestTypeChildChecksIn = 0,
+ kTestTypeChildDoesNotCheckIn_ReadsPipe,
+ kTestTypeChildDoesNotCheckIn,
+ kTestTypeTokenIncorrect,
+ kTestTypeTokenIncorrectThenCorrect,
+ };
+
+ explicit ChildPortHandshakeTest(TestType test_type)
+ : Multiprocess(), child_port_handshake_(), test_type_(test_type) {}
+ ~ChildPortHandshakeTest() {}
+
+ private:
+ // Multiprocess:
+
+ void MultiprocessParent() override {
+ base::mac::ScopedMachSendRight child_port(
+ child_port_handshake_.RunServer());
+ switch (test_type_) {
+ case kTestTypeChildChecksIn:
+ case kTestTypeTokenIncorrectThenCorrect:
+ EXPECT_EQ(bootstrap_port, child_port);
+ break;
+
+ case kTestTypeChildDoesNotCheckIn_ReadsPipe:
+ case kTestTypeChildDoesNotCheckIn:
+ case kTestTypeTokenIncorrect:
+ EXPECT_EQ(kMachPortNull, child_port);
+ break;
+ }
+ }
+
+ void MultiprocessChild() override {
+ int read_pipe = child_port_handshake_.ReadPipeFD();
+ switch (test_type_) {
+ case kTestTypeChildChecksIn:
+ ChildPortHandshake::RunClient(
+ read_pipe, bootstrap_port, MACH_MSG_TYPE_COPY_SEND);
+ break;
+
+ case kTestTypeChildDoesNotCheckIn_ReadsPipe: {
+ // Don’t run the standard client routine. Instead, drain the pipe, which
+ // will get the parent to the point that it begins waiting for a
+ // check-in message. Then, exit. The pipe is drained using the same
+ // implementation that the real client would use.
+ child_port_token_t token;
+ std::string service_name;
+ ChildPortHandshake::RunClientInternal_ReadPipe(
+ read_pipe, &token, &service_name);
+ break;
+ }
+
+ case kTestTypeChildDoesNotCheckIn:
+ break;
+
+ case kTestTypeTokenIncorrect: {
+ // Don’t run the standard client routine. Instead, read the token and
+ // service name, mutate the token, and then check in with the bad token.
+ // The parent should reject the message.
+ child_port_token_t token;
+ std::string service_name;
+ ChildPortHandshake::RunClientInternal_ReadPipe(
+ read_pipe, &token, &service_name);
+ child_port_token_t bad_token = ~token;
+ ChildPortHandshake::RunClientInternal_SendCheckIn(
+ service_name, bad_token, mach_task_self(), MACH_MSG_TYPE_COPY_SEND);
+ break;
+ }
+
+ case kTestTypeTokenIncorrectThenCorrect: {
+ // Don’t run the standard client routine. Instead, read the token and
+ // service name. Mutate the token, and check in with the bad token,
+ // expecting the parent to reject the message. Then, check in with the
+ // correct token, expecting the parent to accept it.
+ child_port_token_t token;
+ std::string service_name;
+ ChildPortHandshake::RunClientInternal_ReadPipe(
+ read_pipe, &token, &service_name);
+ child_port_token_t bad_token = ~token;
+ ChildPortHandshake::RunClientInternal_SendCheckIn(
+ service_name, bad_token, mach_task_self(), MACH_MSG_TYPE_COPY_SEND);
+ ChildPortHandshake::RunClientInternal_SendCheckIn(
+ service_name, token, bootstrap_port, MACH_MSG_TYPE_COPY_SEND);
+ break;
+ }
+ }
+ }
+
+ private:
+ ChildPortHandshake child_port_handshake_;
+ TestType test_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChildPortHandshakeTest);
+};
+
+TEST(ChildPortHandshake, ChildChecksIn) {
+ // In this test, the client checks in with the server normally. It sends a
+ // copy of its bootstrap port to the server, because both parent and child
+ // should have the same bootstrap port, allowing for verification.
+ ChildPortHandshakeTest test(ChildPortHandshakeTest::kTestTypeChildChecksIn);
+ test.Run();
+}
+
+TEST(ChildPortHandshake, ChildDoesNotCheckIn) {
+ // In this test, the client exits without checking in. This tests that the
+ // server properly detects that it has lost a client. Whether or not the
+ // client closes the pipe before the server writes to it is a race, and the
+ // server needs to be able to detect client loss in both cases, so the
+ // ChildDoesNotCheckIn_ReadsPipe and NoChild tests also exist to test these
+ // individual cases more deterministically.
+ ChildPortHandshakeTest test(
+ ChildPortHandshakeTest::kTestTypeChildDoesNotCheckIn);
+ test.Run();
+}
+
+TEST(ChildPortHandshake, ChildDoesNotCheckIn_ReadsPipe) {
+ // In this test, the client reads from its pipe, and subsequently exits
+ // without checking in. This tests that the server properly detects that it
+ // has lost its client after sending instructions to it via the pipe, while
+ // waiting for a check-in message.
+ ChildPortHandshakeTest test(
+ ChildPortHandshakeTest::kTestTypeChildDoesNotCheckIn_ReadsPipe);
+ test.Run();
+}
+
+TEST(ChildPortHandshake, TokenIncorrect) {
+ // In this test, the client checks in with the server with an incorrect token
+ // value and a copy of its own task port. The server should reject the message
+ // because of the invalid token, and return MACH_PORT_NULL to its caller.
+ ChildPortHandshakeTest test(ChildPortHandshakeTest::kTestTypeTokenIncorrect);
+ test.Run();
+}
+
+TEST(ChildPortHandshake, TokenIncorrectThenCorrect) {
+ // In this test, the client checks in with the server with an incorrect token
+ // value and a copy of its own task port, and subsequently, the correct token
+ // value and a copy of its bootstrap port. The server should reject the first
+ // because of the invalid token, but it should continue waiting for a message
+ // with a valid token as long as the pipe remains open. It should wind wind up
+ // returning the bootstrap port, allowing for verification.
+ ChildPortHandshakeTest test(
+ ChildPortHandshakeTest::kTestTypeTokenIncorrectThenCorrect);
+ test.Run();
+}
+
+TEST(ChildPortHandshake, NoChild) {
+ // In this test, the client never checks in with the parent because the child
+ // never even runs. This tests that the server properly detects that it has
+ // no client at all, and does not terminate execution with an error such as
+ // “broken pipe” when attempting to send instructions to the client. This test
+ // is similar to ChildDoesNotCheckIn, but because there’s no child at all, the
+ // server is guaranteed to see that its pipe partner is gone.
+ ChildPortHandshake child_port_handshake;
+ base::mac::ScopedMachSendRight child_port(child_port_handshake.RunServer());
+ EXPECT_EQ(kMachPortNull, child_port);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/child_port_server.cc b/chromium/third_party/crashpad/crashpad/util/mach/child_port_server.cc
new file mode 100644
index 00000000000..4fc723ac7ca
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/child_port_server.cc
@@ -0,0 +1,122 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/child_port_server.h"
+
+#include "base/logging.h"
+#include "util/mach/child_portServer.h"
+#include "util/mach/mach_message.h"
+
+extern "C" {
+
+// This function is not used, and is in fact obsoleted by the other
+// functionality implemented in this file. The standard MIG-generated
+// child_port_server() (in child_portServer.c) server dispatch routine usable
+// with the standard mach_msg_server() function calls out to this function.
+// child_port_server() is unused and is replaced by the more flexible
+// ChildPortServer, but the linker still needs to see this function definition.
+
+kern_return_t handle_child_port_check_in(child_port_server_t server,
+ child_port_token_t token,
+ mach_port_t port,
+ mach_msg_type_name_t right_type) {
+ NOTREACHED();
+ return KERN_FAILURE;
+}
+
+} // extern "C"
+
+namespace {
+
+// There is no predefined constant for this.
+enum MachMessageID : mach_msg_id_t {
+ kMachMessageIDChildPortCheckIn = 10011,
+};
+
+// The MIG-generated __MIG_check__Request__*() functions are not declared as
+// accepting const data, but they could have been because they in fact do not
+// modify the data. This wrapper function is provided to bridge the const gap
+// between the code in this file, which is const-correct and treats request
+// message data as const, and the generated function.
+
+kern_return_t MIGCheckRequestChildPortCheckIn(
+ const __Request__child_port_check_in_t* in_request) {
+ using Request = __Request__child_port_check_in_t;
+ return __MIG_check__Request__child_port_check_in_t(
+ const_cast<Request*>(in_request));
+}
+
+} // namespace
+
+namespace crashpad {
+
+ChildPortServer::ChildPortServer(ChildPortServer::Interface* interface)
+ : MachMessageServer::Interface(),
+ interface_(interface) {
+}
+
+bool ChildPortServer::MachMessageServerFunction(
+ const mach_msg_header_t* in_header,
+ mach_msg_header_t* out_header,
+ bool* destroy_complex_request) {
+ PrepareMIGReplyFromRequest(in_header, out_header);
+
+ const mach_msg_trailer_t* in_trailer =
+ MachMessageTrailerFromHeader(in_header);
+
+ switch (in_header->msgh_id) {
+ case kMachMessageIDChildPortCheckIn: {
+ // child_port_check_in(), handle_child_port_check_in().
+ using Request = __Request__child_port_check_in_t;
+ const Request* in_request = reinterpret_cast<const Request*>(in_header);
+ kern_return_t kr = MIGCheckRequestChildPortCheckIn(in_request);
+ if (kr != MACH_MSG_SUCCESS) {
+ SetMIGReplyError(out_header, kr);
+ return true;
+ }
+
+ using Reply = __Reply__child_port_check_in_t;
+ Reply* out_reply = reinterpret_cast<Reply*>(out_header);
+ out_reply->RetCode =
+ interface_->HandleChildPortCheckIn(in_header->msgh_local_port,
+ in_request->token,
+ in_request->port.name,
+ in_request->port.disposition,
+ in_trailer,
+ destroy_complex_request);
+ return true;
+ }
+
+ default: {
+ SetMIGReplyError(out_header, MIG_BAD_ID);
+ return false;
+ }
+ }
+}
+
+std::set<mach_msg_id_t> ChildPortServer::MachMessageServerRequestIDs() {
+ const mach_msg_id_t request_ids[] = {kMachMessageIDChildPortCheckIn};
+ return std::set<mach_msg_id_t>(&request_ids[0],
+ &request_ids[arraysize(request_ids)]);
+}
+
+mach_msg_size_t ChildPortServer::MachMessageServerRequestSize() {
+ return sizeof(__RequestUnion__handle_child_port_subsystem);
+}
+
+mach_msg_size_t ChildPortServer::MachMessageServerReplySize() {
+ return sizeof(__ReplyUnion__handle_child_port_subsystem);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/child_port_server.h b/chromium/third_party/crashpad/crashpad/util/mach/child_port_server.h
new file mode 100644
index 00000000000..bc42cc094f5
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/child_port_server.h
@@ -0,0 +1,77 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MACH_CHILD_PORT_SERVER_H_
+#define CRASHPAD_UTIL_MACH_CHILD_PORT_SERVER_H_
+
+#include <mach/mach.h>
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "util/mach/child_port_types.h"
+#include "util/mach/mach_message_server.h"
+
+namespace crashpad {
+
+//! \brief A server interface for the `child_port` Mach subsystem.
+class ChildPortServer : public MachMessageServer::Interface {
+ public:
+ //! \brief An interface that the request message that is a part of the
+ //! `child_port` Mach subsystem can be dispatched to.
+ class Interface {
+ public:
+ //! \brief Handles check-ins sent by `child_port_check_in()`.
+ //!
+ //! This behaves equivalently to a `handle_child_port_check_in()` function
+ //! used with `child_port_server()`.
+ //!
+ //! \param[in] trailer The trailer received with the request message.
+ //! \param[out] destroy_request `true` if the request message is to be
+ //! destroyed even when this method returns success. See
+ //! MachMessageServer::Interface.
+ virtual kern_return_t HandleChildPortCheckIn(
+ child_port_server_t server,
+ const child_port_token_t token,
+ mach_port_t port,
+ mach_msg_type_name_t right_type,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_request) = 0;
+
+ protected:
+ ~Interface() {}
+ };
+
+ //! \brief Constructs an object of this class.
+ //!
+ //! \param[in] interface The interface to dispatch requests to. Weak.
+ explicit ChildPortServer(Interface* interface);
+
+ // MachMessageServer::Interface:
+ bool MachMessageServerFunction(const mach_msg_header_t* in_header,
+ mach_msg_header_t* out_header,
+ bool* destroy_complex_request) override;
+ std::set<mach_msg_id_t> MachMessageServerRequestIDs() override;
+ mach_msg_size_t MachMessageServerRequestSize() override;
+ mach_msg_size_t MachMessageServerReplySize() override;
+
+ private:
+ Interface* interface_; // weak
+
+ DISALLOW_COPY_AND_ASSIGN(ChildPortServer);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MACH_CHILD_PORT_SERVER_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/child_port_server_test.cc b/chromium/third_party/crashpad/crashpad/util/mach/child_port_server_test.cc
new file mode 100644
index 00000000000..7dc2ae085cd
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/child_port_server_test.cc
@@ -0,0 +1,136 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/child_port_server.h"
+
+#include <string.h>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "util/mach/mach_extensions.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+using testing::Eq;
+using testing::Pointee;
+using testing::Return;
+
+// Fake Mach ports. These aren’t used as ports in these tests, they’re just used
+// as cookies to make sure that the correct values get passed to the correct
+// places.
+const mach_port_t kServerLocalPort = 0x05050505;
+const mach_port_t kCheckInPort = 0x06060606;
+
+// Other fake values.
+const mach_msg_type_name_t kCheckInPortRightType = MACH_MSG_TYPE_PORT_SEND;
+const child_port_token_t kCheckInToken = 0xfedcba9876543210;
+
+// The definition of the request structure from child_port.h isn’t available
+// here. It needs custom initialization code, so duplicate the expected
+// definition of the structure from child_port.h here in this file, and provide
+// the initialization code as a method in true object-oriented fashion.
+
+struct __attribute__((packed, aligned(4))) ChildPortCheckInRequest {
+ ChildPortCheckInRequest() {
+ memset(this, 0xa5, sizeof(*this));
+ Head.msgh_bits =
+ MACH_MSGH_BITS(0, MACH_MSG_TYPE_PORT_SEND) | MACH_MSGH_BITS_COMPLEX;
+ Head.msgh_size = sizeof(*this) - sizeof(trailer);
+ Head.msgh_remote_port = MACH_PORT_NULL;
+ Head.msgh_local_port = kServerLocalPort;
+ Head.msgh_id = 10011;
+ msgh_body.msgh_descriptor_count = 1;
+ port.name = kCheckInPort;
+ port.disposition = kCheckInPortRightType;
+ port.type = MACH_MSG_PORT_DESCRIPTOR;
+ NDR = NDR_record;
+ token = kCheckInToken;
+ }
+
+ mach_msg_header_t Head;
+ mach_msg_body_t msgh_body;
+ mach_msg_port_descriptor_t port;
+ NDR_record_t NDR;
+ child_port_token_t token;
+ mach_msg_trailer_t trailer;
+};
+
+struct MIGReply : public mig_reply_error_t {
+ MIGReply() {
+ memset(this, 0x5a, sizeof(*this));
+ RetCode = KERN_FAILURE;
+ }
+
+ void Verify() {
+ EXPECT_EQ(implicit_cast<mach_msg_bits_t>(MACH_MSGH_BITS(0, 0)),
+ Head.msgh_bits);
+ EXPECT_EQ(sizeof(*this), Head.msgh_size);
+ EXPECT_EQ(kMachPortNull, Head.msgh_remote_port);
+ EXPECT_EQ(kMachPortNull, Head.msgh_local_port);
+ EXPECT_EQ(10111, Head.msgh_id);
+ EXPECT_EQ(0, memcmp(&NDR, &NDR_record, sizeof(NDR)));
+ EXPECT_EQ(MIG_NO_REPLY, RetCode);
+ }
+};
+
+class MockChildPortServerInterface : public ChildPortServer::Interface {
+ public:
+ MOCK_METHOD6(HandleChildPortCheckIn,
+ kern_return_t(child_port_server_t server,
+ const child_port_token_t token,
+ mach_port_t port,
+ mach_msg_type_name_t right_type,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_request));
+};
+
+TEST(ChildPortServer, MockChildPortCheckIn) {
+ MockChildPortServerInterface server_interface;
+ ChildPortServer server(&server_interface);
+
+ std::set<mach_msg_id_t> expect_request_ids;
+ expect_request_ids.insert(10011); // There is no constant for this.
+ EXPECT_EQ(expect_request_ids, server.MachMessageServerRequestIDs());
+
+ ChildPortCheckInRequest request;
+ EXPECT_EQ(request.Head.msgh_size, server.MachMessageServerRequestSize());
+
+ MIGReply reply;
+ EXPECT_EQ(sizeof(reply), server.MachMessageServerReplySize());
+
+ EXPECT_CALL(server_interface,
+ HandleChildPortCheckIn(kServerLocalPort,
+ kCheckInToken,
+ kCheckInPort,
+ kCheckInPortRightType,
+ Eq(&request.trailer),
+ Pointee(Eq(false))))
+ .WillOnce(Return(MIG_NO_REPLY))
+ .RetiresOnSaturation();
+
+ bool destroy_request = false;
+ EXPECT_TRUE(server.MachMessageServerFunction(
+ reinterpret_cast<mach_msg_header_t*>(&request),
+ reinterpret_cast<mach_msg_header_t*>(&reply),
+ &destroy_request));
+ EXPECT_FALSE(destroy_request);
+
+ reply.Verify();
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/child_port_types.h b/chromium/third_party/crashpad/crashpad/util/mach/child_port_types.h
new file mode 100644
index 00000000000..d0760a73f4b
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/child_port_types.h
@@ -0,0 +1,26 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MACH_CHILD_PORT_TYPES_H_
+#define CRASHPAD_UTIL_MACH_CHILD_PORT_TYPES_H_
+
+#include <mach/mach.h>
+#include <stdint.h>
+
+// This file is #included by C (non-C++) files, and must remain strictly C.
+
+typedef mach_port_t child_port_server_t;
+typedef uint64_t child_port_token_t;
+
+#endif // CRASHPAD_UTIL_MACH_CHILD_PORT_TYPES_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/composite_mach_message_server.cc b/chromium/third_party/crashpad/crashpad/util/mach/composite_mach_message_server.cc
new file mode 100644
index 00000000000..abbe5bcf388
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/composite_mach_message_server.cc
@@ -0,0 +1,86 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/composite_mach_message_server.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/logging.h"
+#include "util/mach/mach_message.h"
+
+namespace crashpad {
+
+CompositeMachMessageServer::CompositeMachMessageServer()
+ : MachMessageServer::Interface(),
+ handler_map_(),
+ request_size_(sizeof(mach_msg_header_t)),
+ reply_size_(sizeof(mig_reply_error_t)) {
+}
+
+CompositeMachMessageServer::~CompositeMachMessageServer() {
+}
+
+void CompositeMachMessageServer::AddHandler(
+ MachMessageServer::Interface* handler) {
+ // Other cycles would be invalid as well, but they aren’t currently checked.
+ DCHECK_NE(handler, this);
+
+ std::set<mach_msg_id_t> request_ids = handler->MachMessageServerRequestIDs();
+ for (mach_msg_id_t request_id : request_ids) {
+ std::pair<HandlerMap::const_iterator, bool> result =
+ handler_map_.insert(std::make_pair(request_id, handler));
+ CHECK(result.second) << "duplicate request ID " << request_id;
+ }
+
+ request_size_ =
+ std::max(request_size_, handler->MachMessageServerRequestSize());
+ reply_size_ = std::max(reply_size_, handler->MachMessageServerReplySize());
+}
+
+bool CompositeMachMessageServer::MachMessageServerFunction(
+ const mach_msg_header_t* in,
+ mach_msg_header_t* out,
+ bool* destroy_complex_request) {
+ HandlerMap::const_iterator iterator = handler_map_.find(in->msgh_id);
+ if (iterator == handler_map_.end()) {
+ // Do what MIG-generated server routines do when they can’t dispatch a
+ // message.
+ PrepareMIGReplyFromRequest(in, out);
+ SetMIGReplyError(out, MIG_BAD_ID);
+ return false;
+ }
+
+ MachMessageServer::Interface* handler = iterator->second;
+ return handler->MachMessageServerFunction(in, out, destroy_complex_request);
+}
+
+std::set<mach_msg_id_t>
+CompositeMachMessageServer::MachMessageServerRequestIDs() {
+ std::set<mach_msg_id_t> request_ids;
+ for (const auto& entry : handler_map_) {
+ request_ids.insert(entry.first);
+ }
+ return request_ids;
+}
+
+mach_msg_size_t CompositeMachMessageServer::MachMessageServerRequestSize() {
+ return request_size_;
+}
+
+mach_msg_size_t CompositeMachMessageServer::MachMessageServerReplySize() {
+ return reply_size_;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/composite_mach_message_server.h b/chromium/third_party/crashpad/crashpad/util/mach/composite_mach_message_server.h
new file mode 100644
index 00000000000..6da957b7ffb
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/composite_mach_message_server.h
@@ -0,0 +1,103 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MACH_COMPOSITE_MACH_MESSAGE_SERVER_H_
+#define CRASHPAD_UTIL_MACH_COMPOSITE_MACH_MESSAGE_SERVER_H_
+
+#include <mach/mach.h>
+
+#include <map>
+#include <set>
+
+#include "base/basictypes.h"
+#include "util/mach/mach_message_server.h"
+
+namespace crashpad {
+
+//! \brief Adapts multiple MachMessageServer::Interface implementations for
+//! simultaneous use in a single MachMessageServer::Run() call.
+//!
+//! This class implements a MachMessageServer::Interface that contains other
+//! other MachMessageServer::Interface objects.
+//!
+//! In some situations, it may be desirable for a Mach message server to handle
+//! messages from distinct MIG subsystems with distinct
+//! MachMessageServer::Interface implementations. This may happen if a single
+//! receive right is shared for multiple subsystems, or if distinct receive
+//! rights are combined in a Mach port set. In these cases, this class performs
+//! a first-level demultiplexing to forward request messages to the proper
+//! subsystem-level demultiplexers.
+class CompositeMachMessageServer : public MachMessageServer::Interface {
+ public:
+ CompositeMachMessageServer();
+ ~CompositeMachMessageServer();
+
+ //! \brief Adds a handler that messages can be dispatched to based on request
+ //! message ID.
+ //!
+ //! \param[in] handler A MachMessageServer handler. Ownership of this object
+ //! is not taken. Cycles must not be created between objects. It is
+ //! invalid to add an object as its own handler.
+ //!
+ //! If \a handler claims to support any request ID that this object is already
+ //! able to handle, execution will be terminated.
+ void AddHandler(MachMessageServer::Interface* handler);
+
+ // MachMessageServer::Interface:
+
+ //! \copydoc MachMessageServer::Interface::MachMessageServerFunction()
+ //!
+ //! This implementation forwards the message to an appropriate handler added
+ //! by AddHandler() on the basis of the \a in request message’s message ID.
+ //! If no appropriate handler exists, the \a out reply message is treated as
+ //! a `mig_reply_error_t`, its return code is set to `MIG_BAD_ID`, and `false`
+ //! is returned.
+ bool MachMessageServerFunction(const mach_msg_header_t* in,
+ mach_msg_header_t* out,
+ bool* destroy_complex_request) override;
+
+ //! \copydoc MachMessageServer::Interface::MachMessageServerRequestIDs()
+ //!
+ //! This implementation returns the set of all request message Mach message
+ //! IDs of all handlers added by AddHandler().
+ std::set<mach_msg_id_t> MachMessageServerRequestIDs() override;
+
+ //! \copydoc MachMessageServer::Interface::MachMessageServerRequestSize()
+ //!
+ //! This implementation returns the maximum request message size of all
+ //! handlers added by AddHandler(). If no handlers are present, returns the
+ //! size of `mach_msg_header_t`, the minimum size of a MIG request message
+ //! that can be received for demultiplexing purposes.
+ mach_msg_size_t MachMessageServerRequestSize() override;
+
+ //! \copydoc MachMessageServer::Interface::MachMessageServerReplySize()
+ //!
+ //! This implementation returns the maximum reply message size of all handlers
+ //! added by AddHandler(). If no handlers are present, returns the size of
+ //! `mig_reply_error_t`, the minimum size of a MIG reply message.
+ mach_msg_size_t MachMessageServerReplySize() override;
+
+ private:
+ using HandlerMap = std::map<mach_msg_id_t, MachMessageServer::Interface*>;
+
+ HandlerMap handler_map_; // weak
+ mach_msg_size_t request_size_;
+ mach_msg_size_t reply_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(CompositeMachMessageServer);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MACH_COMPOSITE_MACH_MESSAGE_SERVER_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/composite_mach_message_server_test.cc b/chromium/third_party/crashpad/crashpad/util/mach/composite_mach_message_server_test.cc
new file mode 100644
index 00000000000..0af82067e15
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/composite_mach_message_server_test.cc
@@ -0,0 +1,304 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/composite_mach_message_server.h"
+
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+#include "test/gtest_death_check.h"
+#include "util/mach/mach_message.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(CompositeMachMessageServer, Empty) {
+ CompositeMachMessageServer server;
+
+ EXPECT_TRUE(server.MachMessageServerRequestIDs().empty());
+
+ mach_msg_empty_rcv_t request = {};
+ EXPECT_EQ(sizeof(request.header), server.MachMessageServerRequestSize());
+
+ mig_reply_error_t reply = {};
+ EXPECT_EQ(sizeof(reply), server.MachMessageServerReplySize());
+
+ bool destroy_complex_request = false;
+ EXPECT_FALSE(server.MachMessageServerFunction(
+ &request.header, &reply.Head, &destroy_complex_request));
+ EXPECT_EQ(MIG_BAD_ID, reply.RetCode);
+}
+
+class TestMachMessageHandler : public MachMessageServer::Interface {
+ public:
+ TestMachMessageHandler()
+ : MachMessageServer::Interface(),
+ request_ids_(),
+ request_size_(0),
+ reply_size_(0),
+ return_code_(KERN_FAILURE),
+ return_value_(false),
+ destroy_complex_request_(false) {
+ }
+
+ ~TestMachMessageHandler() {
+ }
+
+ void SetReturnCodes(bool return_value,
+ kern_return_t return_code,
+ bool destroy_complex_request) {
+ return_value_ = return_value;
+ return_code_ = return_code;
+ destroy_complex_request_ = destroy_complex_request;
+ }
+
+ void AddRequestID(mach_msg_id_t request_id) {
+ request_ids_.insert(request_id);
+ }
+
+ void SetRequestSize(mach_msg_size_t request_size) {
+ request_size_ = request_size;
+ }
+
+ void SetReplySize(mach_msg_size_t reply_size) {
+ reply_size_ = reply_size;
+ }
+
+ // MachMessageServer::Interface:
+
+ bool MachMessageServerFunction(const mach_msg_header_t* in,
+ mach_msg_header_t* out,
+ bool* destroy_complex_request) override {
+ EXPECT_NE(request_ids_.end(), request_ids_.find(in->msgh_id));
+
+ *destroy_complex_request = destroy_complex_request_;
+ PrepareMIGReplyFromRequest(in, out);
+ SetMIGReplyError(out, return_code_);
+ return return_value_;
+ }
+
+ std::set<mach_msg_id_t> MachMessageServerRequestIDs() override {
+ return request_ids_;
+ }
+
+ mach_msg_size_t MachMessageServerRequestSize() override {
+ return request_size_;
+ }
+
+ mach_msg_size_t MachMessageServerReplySize() override {
+ return reply_size_;
+ }
+
+ private:
+ std::set<mach_msg_id_t> request_ids_;
+ mach_msg_size_t request_size_;
+ mach_msg_size_t reply_size_;
+ kern_return_t return_code_;
+ bool return_value_;
+ bool destroy_complex_request_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestMachMessageHandler);
+};
+
+TEST(CompositeMachMessageServer, HandlerDoesNotHandle) {
+ TestMachMessageHandler handler;
+
+ CompositeMachMessageServer server;
+ server.AddHandler(&handler);
+
+ EXPECT_TRUE(server.MachMessageServerRequestIDs().empty());
+
+ mach_msg_empty_rcv_t request = {};
+ EXPECT_EQ(sizeof(request.header), server.MachMessageServerRequestSize());
+
+ mig_reply_error_t reply = {};
+ EXPECT_EQ(sizeof(reply), server.MachMessageServerReplySize());
+
+ bool destroy_complex_request = false;
+ EXPECT_FALSE(server.MachMessageServerFunction(
+ &request.header, &reply.Head, &destroy_complex_request));
+ EXPECT_EQ(MIG_BAD_ID, reply.RetCode);
+ EXPECT_FALSE(destroy_complex_request);
+}
+
+TEST(CompositeMachMessageServer, OneHandler) {
+ const mach_msg_id_t kRequestID = 100;
+ const mach_msg_size_t kRequestSize = 256;
+ const mach_msg_size_t kReplySize = 128;
+ const kern_return_t kReturnCode = KERN_SUCCESS;
+
+ TestMachMessageHandler handler;
+ handler.AddRequestID(kRequestID);
+ handler.SetRequestSize(kRequestSize);
+ handler.SetReplySize(kReplySize);
+ handler.SetReturnCodes(true, kReturnCode, true);
+
+ CompositeMachMessageServer server;
+
+ // The chosen request and reply sizes must be larger than the defaults for
+ // that portion fo the test to be valid.
+ EXPECT_GT(kRequestSize, server.MachMessageServerRequestSize());
+ EXPECT_GT(kReplySize, server.MachMessageServerReplySize());
+
+ server.AddHandler(&handler);
+
+ std::set<mach_msg_id_t> expect_request_ids;
+ expect_request_ids.insert(kRequestID);
+ EXPECT_EQ(expect_request_ids, server.MachMessageServerRequestIDs());
+
+ EXPECT_EQ(kRequestSize, server.MachMessageServerRequestSize());
+ EXPECT_EQ(kReplySize, server.MachMessageServerReplySize());
+
+ mach_msg_empty_rcv_t request = {};
+ mig_reply_error_t reply = {};
+
+ // Send a message with an unknown request ID.
+ request.header.msgh_id = 0;
+ bool destroy_complex_request = false;
+ EXPECT_FALSE(server.MachMessageServerFunction(
+ &request.header, &reply.Head, &destroy_complex_request));
+ EXPECT_EQ(MIG_BAD_ID, reply.RetCode);
+ EXPECT_FALSE(destroy_complex_request);
+
+ // Send a message with a known request ID.
+ request.header.msgh_id = kRequestID;
+ EXPECT_TRUE(server.MachMessageServerFunction(
+ &request.header, &reply.Head, &destroy_complex_request));
+ EXPECT_EQ(kReturnCode, reply.RetCode);
+ EXPECT_TRUE(destroy_complex_request);
+}
+
+TEST(CompositeMachMessageServer, ThreeHandlers) {
+ const mach_msg_id_t kRequestIDs0[] = {5};
+ const kern_return_t kReturnCode0 = KERN_SUCCESS;
+
+ const mach_msg_id_t kRequestIDs1[] = {4, 7};
+ const kern_return_t kReturnCode1 = KERN_PROTECTION_FAILURE;
+
+ const mach_msg_id_t kRequestIDs2[] = {10, 0, 20};
+ const mach_msg_size_t kRequestSize2 = 6144;
+ const mach_msg_size_t kReplySize2 = 16384;
+ const kern_return_t kReturnCode2 = KERN_NOT_RECEIVER;
+
+ TestMachMessageHandler handlers[3];
+ std::set<mach_msg_id_t> expect_request_ids;
+
+ for (size_t index = 0; index < arraysize(kRequestIDs0); ++index) {
+ const mach_msg_id_t request_id = kRequestIDs0[index];
+ handlers[0].AddRequestID(request_id);
+ expect_request_ids.insert(request_id);
+ }
+ handlers[0].SetRequestSize(sizeof(mach_msg_header_t));
+ handlers[0].SetReplySize(sizeof(mig_reply_error_t));
+ handlers[0].SetReturnCodes(true, kReturnCode0, false);
+
+ for (size_t index = 0; index < arraysize(kRequestIDs1); ++index) {
+ const mach_msg_id_t request_id = kRequestIDs1[index];
+ handlers[1].AddRequestID(request_id);
+ expect_request_ids.insert(request_id);
+ }
+ handlers[1].SetRequestSize(100);
+ handlers[1].SetReplySize(200);
+ handlers[1].SetReturnCodes(false, kReturnCode1, true);
+
+ for (size_t index = 0; index < arraysize(kRequestIDs2); ++index) {
+ const mach_msg_id_t request_id = kRequestIDs2[index];
+ handlers[2].AddRequestID(request_id);
+ expect_request_ids.insert(request_id);
+ }
+ handlers[2].SetRequestSize(kRequestSize2);
+ handlers[2].SetReplySize(kReplySize2);
+ handlers[2].SetReturnCodes(true, kReturnCode2, true);
+
+ CompositeMachMessageServer server;
+
+ // The chosen request and reply sizes must be larger than the defaults for
+ // that portion fo the test to be valid.
+ EXPECT_GT(kRequestSize2, server.MachMessageServerRequestSize());
+ EXPECT_GT(kReplySize2, server.MachMessageServerReplySize());
+
+ server.AddHandler(&handlers[0]);
+ server.AddHandler(&handlers[1]);
+ server.AddHandler(&handlers[2]);
+
+ EXPECT_EQ(expect_request_ids, server.MachMessageServerRequestIDs());
+
+ EXPECT_EQ(kRequestSize2, server.MachMessageServerRequestSize());
+ EXPECT_EQ(kReplySize2, server.MachMessageServerReplySize());
+
+ mach_msg_empty_rcv_t request = {};
+ mig_reply_error_t reply = {};
+
+ // Send a message with an unknown request ID.
+ request.header.msgh_id = 100;
+ bool destroy_complex_request = false;
+ EXPECT_FALSE(server.MachMessageServerFunction(
+ &request.header, &reply.Head, &destroy_complex_request));
+ EXPECT_EQ(MIG_BAD_ID, reply.RetCode);
+ EXPECT_FALSE(destroy_complex_request);
+
+ // Send messages with known request IDs.
+
+ for (size_t index = 0; index < arraysize(kRequestIDs0); ++index) {
+ request.header.msgh_id = kRequestIDs0[index];
+ SCOPED_TRACE(base::StringPrintf(
+ "handler 0, index %zu, id %d", index, request.header.msgh_id));
+
+ EXPECT_TRUE(server.MachMessageServerFunction(
+ &request.header, &reply.Head, &destroy_complex_request));
+ EXPECT_EQ(kReturnCode0, reply.RetCode);
+ EXPECT_FALSE(destroy_complex_request);
+ }
+
+ for (size_t index = 0; index < arraysize(kRequestIDs1); ++index) {
+ request.header.msgh_id = kRequestIDs1[index];
+ SCOPED_TRACE(base::StringPrintf(
+ "handler 1, index %zu, id %d", index, request.header.msgh_id));
+
+ EXPECT_FALSE(server.MachMessageServerFunction(
+ &request.header, &reply.Head, &destroy_complex_request));
+ EXPECT_EQ(kReturnCode1, reply.RetCode);
+ EXPECT_TRUE(destroy_complex_request);
+ }
+
+ for (size_t index = 0; index < arraysize(kRequestIDs2); ++index) {
+ request.header.msgh_id = kRequestIDs2[index];
+ SCOPED_TRACE(base::StringPrintf(
+ "handler 2, index %zu, id %d", index, request.header.msgh_id));
+
+ EXPECT_TRUE(server.MachMessageServerFunction(
+ &request.header, &reply.Head, &destroy_complex_request));
+ EXPECT_EQ(kReturnCode2, reply.RetCode);
+ EXPECT_TRUE(destroy_complex_request);
+ }
+}
+
+// CompositeMachMessageServer can’t deal with two handlers that want to handle
+// the same request ID.
+TEST(CompositeMachMessageServerDeathTest, DuplicateRequestID) {
+ const mach_msg_id_t kRequestID = 400;
+
+ TestMachMessageHandler handlers[2];
+ handlers[0].AddRequestID(kRequestID);
+ handlers[1].AddRequestID(kRequestID);
+
+ CompositeMachMessageServer server;
+
+ server.AddHandler(&handlers[0]);
+ EXPECT_DEATH_CHECK(server.AddHandler(&handlers[1]), "duplicate request ID");
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/exc_client_variants.cc b/chromium/third_party/crashpad/crashpad/util/mach/exc_client_variants.cc
new file mode 100644
index 00000000000..71e39691ace
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/exc_client_variants.cc
@@ -0,0 +1,129 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/exc_client_variants.h"
+
+#include <vector>
+
+#include "base/logging.h"
+#include "util/mach/exc.h"
+#include "util/mach/mach_exc.h"
+
+namespace crashpad {
+
+kern_return_t UniversalExceptionRaise(exception_behavior_t behavior,
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count) {
+ // This function is similar to 10.9.4 xnu-2422.110.17/osfmk/kern/exception.c
+ // exception_deliver() as far as the delivery logic is concerned. Unlike
+ // exception_deliver(), this function does not get or set thread states for
+ // behavior values that require this, as that is left to the caller to do if
+ // needed.
+
+ std::vector<exception_data_type_t> small_code_vector;
+ exception_data_t small_code = nullptr;
+ if ((behavior & MACH_EXCEPTION_CODES) == 0 && code_count) {
+ small_code_vector.reserve(code_count);
+ for (size_t code_index = 0; code_index < code_count; ++code_index) {
+ small_code_vector[code_index] = code[code_index];
+ }
+ small_code = &small_code_vector[0];
+ }
+
+ // The *exception_raise*() family has bad declarations. Their code and
+ // old_state arguments aren’t pointers to const data, although they should be.
+ // The generated stubs in excUser.c and mach_excUser.c make it clear that the
+ // data is never modified, and these parameters could be declared with const
+ // appropriately. The uses of const_cast below are thus safe.
+
+ switch (behavior) {
+ case EXCEPTION_DEFAULT:
+ return exception_raise(
+ exception_port, thread, task, exception, small_code, code_count);
+
+ case EXCEPTION_STATE:
+ return exception_raise_state(exception_port,
+ exception,
+ small_code,
+ code_count,
+ flavor,
+ const_cast<thread_state_t>(old_state),
+ old_state_count,
+ new_state,
+ new_state_count);
+
+ case EXCEPTION_STATE_IDENTITY:
+ return exception_raise_state_identity(
+ exception_port,
+ thread,
+ task,
+ exception,
+ small_code,
+ code_count,
+ flavor,
+ const_cast<thread_state_t>(old_state),
+ old_state_count,
+ new_state,
+ new_state_count);
+
+ case EXCEPTION_DEFAULT | kMachExceptionCodes:
+ return mach_exception_raise(exception_port,
+ thread,
+ task,
+ exception,
+ const_cast<mach_exception_data_type_t*>(code),
+ code_count);
+
+ case EXCEPTION_STATE | kMachExceptionCodes:
+ return mach_exception_raise_state(
+ exception_port,
+ exception,
+ const_cast<mach_exception_data_type_t*>(code),
+ code_count,
+ flavor,
+ const_cast<thread_state_t>(old_state),
+ old_state_count,
+ new_state,
+ new_state_count);
+
+ case EXCEPTION_STATE_IDENTITY | kMachExceptionCodes:
+ return mach_exception_raise_state_identity(
+ exception_port,
+ thread,
+ task,
+ exception,
+ const_cast<mach_exception_data_type_t*>(code),
+ code_count,
+ flavor,
+ const_cast<thread_state_t>(old_state),
+ old_state_count,
+ new_state,
+ new_state_count);
+
+ default:
+ NOTREACHED();
+ return KERN_INVALID_ARGUMENT;
+ }
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/exc_client_variants.h b/chromium/third_party/crashpad/crashpad/util/mach/exc_client_variants.h
new file mode 100644
index 00000000000..d96e689ea23
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/exc_client_variants.h
@@ -0,0 +1,85 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MACH_EXC_CLIENT_VARIANTS_H_
+#define CRASHPAD_UTIL_MACH_EXC_CLIENT_VARIANTS_H_
+
+#include <mach/mach.h>
+
+#include "util/mach/mach_extensions.h"
+
+namespace crashpad {
+
+//! \brief Calls the appropriate `*exception_raise*()` function for the
+//! specified \a behavior.
+//!
+//! The function called will be `exception_raise()` for `EXCEPTION_DEFAULT`,
+//! `exception_raise_state()` for `EXCEPTION_STATE`, or
+//! `exception_raise_state_identity()` for `EXCEPTION_STATE_IDENTITY`. If
+//! `MACH_EXCEPTION_CODES` is also set, the function called will instead be
+//! `mach_exception_raise()`, `mach_exception_raise_state()` or
+//! `mach_exception_raise_state_identity()`, respectively.
+//!
+//! This function does not fetch the existing thread state for \a behavior
+//! values that require a thread state. The caller must provide the existing
+//! thread state in the \a flavor, \a old_state, and \a old_state_count
+//! parameters for \a behavior values that require a thread state. Thread states
+//! may be obtained by calling `thread_get_state()` if needed. Similarly, this
+//! function does not do anything with the new thread state returned for these
+//! \a behavior values. Callers that wish to make use of the new thread state
+//! may do so by using the returned \a flavor, \a new_state, and \a
+//! new_state_count values. Thread states may be set by calling
+//! `thread_set_state()` if needed.
+//!
+//! \a thread and \a task are only used when \a behavior indicates that the
+//! exception message will carry identity information, when it has the value
+//! value `EXCEPTION_DEFAULT` or `EXCEPTION_STATE_IDENTITY`, possibly with
+//! `MACH_EXCEPTION_CODES` also set. In other cases, these parameters are unused
+//! and may be set to `THREAD_NULL` and `TASK_NULL`, respectively.
+//!
+//! \a flavor, \a old_state, \a old_state_count, \a new_state, and \a
+//! new_state_count are only used when \a behavior indicates that the exception
+//! message will carry thread state information, when it has the value
+//! `EXCEPTION_STATE` or `EXCEPTION_STATE_IDENTITY`, possibly with
+//! `MACH_EXCEPTION_CODES` also set. In other cases, these parameters are unused
+//! and may be set to `0` (\a old_state_count) or `nullptr` (the remaining
+//! parameters).
+//!
+//! \param[in] behavior The exception behavior, which dictates which function
+//! will be called. It is an error to call this function with an invalid
+//! value for \a behavior.
+//! \param[in] code If \behavior indicates a behavior without
+//! `MACH_EXCEPTION_CODES`, the elements of \a code will be truncated in
+//! order to be passed to the appropriate exception handler.
+//!
+//! All other parameters are treated equivalently to their treatment by the
+//! `*exception_raise*()` family of functions.
+//!
+//! \return The return value of the function called.
+kern_return_t UniversalExceptionRaise(exception_behavior_t behavior,
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count);
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MACH_EXC_CLIENT_VARIANTS_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/exc_client_variants_test.cc b/chromium/third_party/crashpad/crashpad/util/mach/exc_client_variants_test.cc
new file mode 100644
index 00000000000..b3d34ed0b93
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/exc_client_variants_test.cc
@@ -0,0 +1,295 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/exc_client_variants.h"
+
+#include <mach/mach.h>
+#include <pthread.h>
+#include <string.h>
+
+#include "base/basictypes.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+#include "test/mac/mach_errors.h"
+#include "test/mac/mach_multiprocess.h"
+#include "util/mach/exc_server_variants.h"
+#include "util/mach/exception_behaviors.h"
+#include "util/mach/mach_extensions.h"
+#include "util/mach/mach_message.h"
+#include "util/mach/mach_message_server.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+class TestExcClientVariants : public MachMultiprocess,
+ public UniversalMachExcServer::Interface {
+ public:
+ TestExcClientVariants(exception_behavior_t behavior, bool all_fields)
+ : MachMultiprocess(),
+ UniversalMachExcServer::Interface(),
+ behavior_(behavior),
+ all_fields_(all_fields),
+ handled_(false) {
+ ++exception_;
+ ++exception_code_;
+ ++exception_subcode_;
+ }
+
+ // UniversalMachExcServer::Interface:
+
+ virtual kern_return_t CatchMachException(
+ exception_behavior_t behavior,
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_complex_request) override {
+ *destroy_complex_request = true;
+
+ EXPECT_FALSE(handled_);
+ handled_ = true;
+
+ EXPECT_EQ(behavior_, behavior);
+ EXPECT_EQ(LocalPort(), exception_port);
+
+ if (HasIdentity()) {
+ EXPECT_NE(THREAD_NULL, thread);
+ EXPECT_EQ(ChildTask(), task);
+ } else {
+ EXPECT_EQ(THREAD_NULL, thread);
+ EXPECT_EQ(TASK_NULL, task);
+ }
+
+ mach_exception_code_t expect_code = exception_code_;
+ mach_exception_subcode_t expect_subcode = exception_subcode_;
+ if ((behavior & MACH_EXCEPTION_CODES) == 0) {
+ expect_code = implicit_cast<exception_data_type_t>(expect_code);
+ expect_subcode = implicit_cast<exception_data_type_t>(expect_subcode);
+ }
+
+ EXPECT_EQ(exception_, exception);
+ EXPECT_EQ(2u, code_count);
+
+ // The code_count check above would ideally use ASSERT_EQ so that the next
+ // conditionals would not be necessary, but ASSERT_* requires a function
+ // returning type void, and the interface dictates otherwise here.
+ if (code_count >= 1) {
+ EXPECT_EQ(expect_code, code[0]);
+ }
+ if (code_count >= 2) {
+ EXPECT_EQ(expect_subcode, code[1]);
+ }
+
+ if (HasState()) {
+ EXPECT_EQ(exception_ + 10, *flavor);
+ EXPECT_EQ(MACHINE_THREAD_STATE_COUNT, old_state_count);
+ EXPECT_NE(nullptr, old_state);
+ EXPECT_EQ(implicit_cast<mach_msg_type_number_t>(THREAD_STATE_MAX),
+ *new_state_count);
+ EXPECT_NE(nullptr, new_state);
+
+ for (size_t index = 0; index < old_state_count; ++index) {
+ EXPECT_EQ(index, old_state[index]);
+ }
+
+ // Use a flavor known to be different from the incoming flavor, for a test
+ // of the “out” side of the inout flavor parameter.
+ *flavor = exception_ + 20;
+ *new_state_count = MACHINE_THREAD_STATE_COUNT;
+
+ // Send a new state back to the client.
+ for (size_t index = 0; index < *new_state_count; ++index) {
+ new_state[index] = MACHINE_THREAD_STATE_COUNT - index;
+ }
+ } else {
+ EXPECT_EQ(THREAD_STATE_NONE, *flavor);
+ EXPECT_EQ(0u, old_state_count);
+ EXPECT_EQ(nullptr, old_state);
+ EXPECT_EQ(0u, *new_state_count);
+ EXPECT_EQ(nullptr, new_state);
+ }
+
+ return KERN_SUCCESS;
+ }
+
+ private:
+ // MachMultiprocess:
+
+ void MachMultiprocessParent() override {
+ UniversalMachExcServer universal_mach_exc_server(this);
+
+ kern_return_t kr =
+ MachMessageServer::Run(&universal_mach_exc_server,
+ LocalPort(),
+ MACH_MSG_OPTION_NONE,
+ MachMessageServer::kOneShot,
+ MachMessageServer::kReceiveLargeError,
+ kMachMessageTimeoutWaitIndefinitely);
+ EXPECT_EQ(KERN_SUCCESS, kr)
+ << MachErrorMessage(kr, "MachMessageServer::Run");
+
+ EXPECT_TRUE(handled_);
+ }
+
+ void MachMultiprocessChild() override {
+ const exception_type_t exception = exception_;
+ const mach_exception_data_type_t code[] = {
+ exception_code_,
+ exception_subcode_
+ };
+
+ thread_t thread = THREAD_NULL;
+ task_t task = TASK_NULL;
+ if (all_fields_ || HasIdentity()) {
+ thread = MachThreadSelf();
+ task = mach_task_self();
+ }
+
+ thread_state_flavor_t flavor;
+ thread_state_flavor_t* flavor_p = nullptr;
+ natural_t old_state[MACHINE_THREAD_STATE_COUNT];
+ thread_state_t old_state_p = nullptr;
+ mach_msg_type_number_t old_state_count = 0;
+ natural_t new_state[THREAD_STATE_MAX];
+ thread_state_t new_state_p = nullptr;
+ mach_msg_type_number_t new_state_count;
+ mach_msg_type_number_t* new_state_count_p = nullptr;
+ if (all_fields_ || HasState()) {
+ // Pick a different flavor each time based on the value of exception_.
+ // These aren’t real flavors, it’s just for testing.
+ flavor = exception_ + 10;
+ flavor_p = &flavor;
+ for (size_t index = 0; index < arraysize(old_state); ++index) {
+ old_state[index] = index;
+ }
+ old_state_p = reinterpret_cast<thread_state_t>(&old_state);
+ old_state_count = arraysize(old_state);
+
+ // new_state and new_state_count are out parameters that the server should
+ // never see or use, so set them to bogus values. The call to the server
+ // should overwrite these.
+ memset(new_state, 0xa5, sizeof(new_state));
+ new_state_p = reinterpret_cast<thread_state_t>(&new_state);
+ new_state_count = 0x5a;
+ new_state_count_p = &new_state_count;
+ }
+
+ EXPECT_EQ(KERN_SUCCESS, UniversalExceptionRaise(behavior_,
+ RemotePort(),
+ thread,
+ task,
+ exception,
+ code,
+ arraysize(code),
+ flavor_p,
+ old_state_p,
+ old_state_count,
+ new_state_p,
+ new_state_count_p));
+
+ if (HasState()) {
+ // Verify the out parameters.
+
+ EXPECT_EQ(exception_ + 20, flavor);
+ EXPECT_EQ(MACHINE_THREAD_STATE_COUNT, new_state_count);
+
+ for (size_t index = 0; index < new_state_count; ++index) {
+ EXPECT_EQ(MACHINE_THREAD_STATE_COUNT - index, new_state[index]);
+ }
+ }
+ }
+
+ bool HasIdentity() const {
+ return ExceptionBehaviorHasIdentity(behavior_);
+ }
+
+ bool HasState() const {
+ return ExceptionBehaviorHasState(behavior_);
+ }
+
+ // The behavior to test.
+ exception_behavior_t behavior_;
+
+ // If false, only fields required for the current value of behavior_ are set
+ // in a call to UniversalExceptionRaise(). The thread and task fields are only
+ // set for identity-carrying behaviors, and the flavor and state fields are
+ // only set for state-carrying behaviors. If true, all fields are set
+ // regardless of the behavior. Testing in both ways verifies that
+ // UniversalExceptionRaise() can tolerate the null arguments documented as
+ // usable when the behavior allows it, and that it ignores these arguments
+ // even when set when the behavior does not make use of them.
+ bool all_fields_;
+
+ // true if an exception message was handled.
+ bool handled_;
+
+ // These fields will increment for each instantiation of the test class.
+ static exception_type_t exception_;
+ static mach_exception_code_t exception_code_;
+ static mach_exception_subcode_t exception_subcode_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestExcClientVariants);
+};
+
+exception_type_t TestExcClientVariants::exception_ = 0;
+
+// exception_code_ and exception_subcode_ are always large enough to require
+// 64 bits, so that when the 32-bit-only variants not using MACH_EXCEPITON_CODES
+// are tested, the code and subcode fields can be checked for proper truncation.
+mach_exception_code_t TestExcClientVariants::exception_code_ = 0x100000000;
+mach_exception_subcode_t TestExcClientVariants::exception_subcode_ =
+ 0xffffffff00000000;
+
+TEST(ExcClientVariants, UniversalExceptionRaise) {
+ const exception_behavior_t kBehaviors[] = {
+ EXCEPTION_DEFAULT,
+ EXCEPTION_STATE,
+ EXCEPTION_STATE_IDENTITY,
+ kMachExceptionCodes | EXCEPTION_DEFAULT,
+ kMachExceptionCodes | EXCEPTION_STATE,
+ kMachExceptionCodes | EXCEPTION_STATE_IDENTITY,
+ };
+
+ for (size_t index = 0; index < arraysize(kBehaviors); ++index) {
+ exception_behavior_t behavior = kBehaviors[index];
+ SCOPED_TRACE(base::StringPrintf("index %zu, behavior %d", index, behavior));
+
+ {
+ SCOPED_TRACE("all_fields = false");
+
+ TestExcClientVariants test_exc_client_variants(behavior, false);
+ test_exc_client_variants.Run();
+ }
+
+ {
+ SCOPED_TRACE("all_fields = true");
+
+ TestExcClientVariants test_exc_client_variants(behavior, true);
+ test_exc_client_variants.Run();
+ }
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/exc_server_variants.cc b/chromium/third_party/crashpad/crashpad/util/mach/exc_server_variants.cc
new file mode 100644
index 00000000000..e5916c7db20
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/exc_server_variants.cc
@@ -0,0 +1,790 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/exc_server_variants.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "base/logging.h"
+#include "util/mach/composite_mach_message_server.h"
+#include "util/mach/exc.h"
+#include "util/mach/exception_behaviors.h"
+#include "util/mach/excServer.h"
+#include "util/mach/mach_exc.h"
+#include "util/mach/mach_excServer.h"
+#include "util/mach/mach_message.h"
+
+extern "C" {
+
+// These six functions are not used, and are in fact obsoleted by the other
+// functionality implemented in this file. The standard MIG-generated exc_server
+// (in excServer.c) and mach_exc_server (in mach_excServer.c) server dispatch
+// routines usable with the standard mach_msg_server() function call out to
+// these functions. exc_server() and mach_exc_server() are unused and are
+// replaced by the more flexible ExcServer and MachExcServer, but the linker
+// still needs to see these six function definitions.
+
+kern_return_t catch_exception_raise(exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ exception_data_t code,
+ mach_msg_type_number_t code_count) {
+ NOTREACHED();
+ return KERN_FAILURE;
+}
+
+kern_return_t catch_exception_raise_state(
+ exception_handler_t exception_port,
+ exception_type_t exception,
+ exception_data_t code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ thread_state_t old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count) {
+ NOTREACHED();
+ return KERN_FAILURE;
+}
+
+kern_return_t catch_exception_raise_state_identity(
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ exception_data_t code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ thread_state_t old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count) {
+ NOTREACHED();
+ return KERN_FAILURE;
+}
+
+kern_return_t catch_mach_exception_raise(exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ mach_exception_data_t code,
+ mach_msg_type_number_t code_count) {
+ NOTREACHED();
+ return KERN_FAILURE;
+}
+
+kern_return_t catch_mach_exception_raise_state(
+ exception_handler_t exception_port,
+ exception_type_t exception,
+ mach_exception_data_t code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ thread_state_t old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count) {
+ NOTREACHED();
+ return KERN_FAILURE;
+}
+
+kern_return_t catch_mach_exception_raise_state_identity(
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ mach_exception_data_t code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ thread_state_t old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count) {
+ NOTREACHED();
+ return KERN_FAILURE;
+}
+
+} // extern "C"
+
+namespace crashpad {
+
+namespace {
+
+// Traits for ExcServer<> and SimplifiedExcServer<> adapting them for use with
+// the exc subsystem.
+struct ExcTraits {
+ using ExceptionCode = exception_data_type_t;
+
+ using RequestUnion = __RequestUnion__exc_subsystem;
+ using ReplyUnion = __ReplyUnion__exc_subsystem;
+
+ using ExceptionRaiseRequest = __Request__exception_raise_t;
+ using ExceptionRaiseStateRequest = __Request__exception_raise_state_t;
+ using ExceptionRaiseStateIdentityRequest =
+ __Request__exception_raise_state_identity_t;
+
+ using ExceptionRaiseReply = __Reply__exception_raise_t;
+ using ExceptionRaiseStateReply = __Reply__exception_raise_state_t;
+ using ExceptionRaiseStateIdentityReply =
+ __Reply__exception_raise_state_identity_t;
+
+ // The MIG-generated __MIG_check__Request__*() functions are not declared as
+ // accepting const data, but they could have been because they in fact do not
+ // modify the data.
+
+ static kern_return_t MIGCheckRequestExceptionRaise(
+ const ExceptionRaiseRequest* in_request) {
+ return __MIG_check__Request__exception_raise_t(
+ const_cast<ExceptionRaiseRequest*>(in_request));
+ }
+
+ static kern_return_t MIGCheckRequestExceptionRaiseState(
+ const ExceptionRaiseStateRequest* in_request,
+ const ExceptionRaiseStateRequest** in_request_1) {
+ return __MIG_check__Request__exception_raise_state_t(
+ const_cast<ExceptionRaiseStateRequest*>(in_request),
+ const_cast<ExceptionRaiseStateRequest**>(in_request_1));
+ }
+
+ static kern_return_t MIGCheckRequestExceptionRaiseStateIdentity(
+ const ExceptionRaiseStateIdentityRequest* in_request,
+ const ExceptionRaiseStateIdentityRequest** in_request_1) {
+ return __MIG_check__Request__exception_raise_state_identity_t(
+ const_cast<ExceptionRaiseStateIdentityRequest*>(in_request),
+ const_cast<ExceptionRaiseStateIdentityRequest**>(in_request_1));
+ }
+
+ // There are no predefined constants for these.
+ static const mach_msg_id_t kMachMessageIDExceptionRaise = 2401;
+ static const mach_msg_id_t kMachMessageIDExceptionRaiseState = 2402;
+ static const mach_msg_id_t kMachMessageIDExceptionRaiseStateIdentity = 2403;
+
+ static const exception_behavior_t kExceptionBehavior = 0;
+};
+
+// Traits for ExcServer<> and SimplifiedExcServer<> adapting them for use with
+// the mach_exc subsystem.
+struct MachExcTraits {
+ using ExceptionCode = mach_exception_data_type_t;
+
+ using RequestUnion = __RequestUnion__mach_exc_subsystem;
+ using ReplyUnion = __ReplyUnion__mach_exc_subsystem;
+
+ using ExceptionRaiseRequest = __Request__mach_exception_raise_t;
+ using ExceptionRaiseStateRequest = __Request__mach_exception_raise_state_t;
+ using ExceptionRaiseStateIdentityRequest =
+ __Request__mach_exception_raise_state_identity_t;
+
+ using ExceptionRaiseReply = __Reply__mach_exception_raise_t;
+ using ExceptionRaiseStateReply = __Reply__mach_exception_raise_state_t;
+ using ExceptionRaiseStateIdentityReply =
+ __Reply__mach_exception_raise_state_identity_t;
+
+ // The MIG-generated __MIG_check__Request__*() functions are not declared as
+ // accepting const data, but they could have been because they in fact do not
+ // modify the data.
+
+ static kern_return_t MIGCheckRequestExceptionRaise(
+ const ExceptionRaiseRequest* in_request) {
+ return __MIG_check__Request__mach_exception_raise_t(
+ const_cast<ExceptionRaiseRequest*>(in_request));
+ }
+
+ static kern_return_t MIGCheckRequestExceptionRaiseState(
+ const ExceptionRaiseStateRequest* in_request,
+ const ExceptionRaiseStateRequest** in_request_1) {
+ return __MIG_check__Request__mach_exception_raise_state_t(
+ const_cast<ExceptionRaiseStateRequest*>(in_request),
+ const_cast<ExceptionRaiseStateRequest**>(in_request_1));
+ }
+
+ static kern_return_t MIGCheckRequestExceptionRaiseStateIdentity(
+ const ExceptionRaiseStateIdentityRequest* in_request,
+ const ExceptionRaiseStateIdentityRequest** in_request_1) {
+ return __MIG_check__Request__mach_exception_raise_state_identity_t(
+ const_cast<ExceptionRaiseStateIdentityRequest*>(in_request),
+ const_cast<ExceptionRaiseStateIdentityRequest**>(in_request_1));
+ }
+
+ // There are no predefined constants for these.
+ static const mach_msg_id_t kMachMessageIDExceptionRaise = 2405;
+ static const mach_msg_id_t kMachMessageIDExceptionRaiseState = 2406;
+ static const mach_msg_id_t kMachMessageIDExceptionRaiseStateIdentity = 2407;
+
+ static const exception_behavior_t kExceptionBehavior = MACH_EXCEPTION_CODES;
+};
+
+//! \brief A server interface for the `exc` or `mach_exc` Mach subsystems.
+template <typename Traits>
+class ExcServer : public MachMessageServer::Interface {
+ public:
+ //! \brief An interface that the different request messages that are a part of
+ //! the `exc` or `mach_exc` Mach subsystems can be dispatched to.
+ class Interface {
+ public:
+ //! \brief Handles exceptions raised by `exception_raise()` or
+ //! `mach_exception_raise()`.
+ //!
+ //! This behaves equivalently to a `catch_exception_raise()` function used
+ //! with `exc_server()`, or a `catch_mach_exception_raise()` function used
+ //! with `mach_exc_server()`.
+ //!
+ //! \param[in] trailer The trailer received with the request message.
+ //! \param[out] destroy_request `true` if the request message is to be
+ //! destroyed even when this method returns success. See
+ //! MachMessageServer::Interface.
+ virtual kern_return_t CatchExceptionRaise(
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const typename Traits::ExceptionCode* code,
+ mach_msg_type_number_t code_count,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_request) = 0;
+
+ //! \brief Handles exceptions raised by `exception_raise_state()` or
+ //! `mach_exception_raise_state()`.
+ //!
+ //! This behaves equivalently to a `catch_exception_raise_state()` function
+ //! used with `exc_server()`, or a `catch_mach_exception_raise_state()`
+ //! function used with `mach_exc_server()`.
+ //!
+ //! There is no \a destroy_request parameter because, unlike
+ //! CatchExceptionRaise() and CatchExceptionRaiseStateIdentity(), the
+ //! request message is not complex (it does not carry the \a thread or \a
+ //! task port rights) and thus there is nothing to destroy.
+ //!
+ //! \param[in] trailer The trailer received with the request message.
+ virtual kern_return_t CatchExceptionRaiseState(
+ exception_handler_t exception_port,
+ exception_type_t exception,
+ const typename Traits::ExceptionCode* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count,
+ const mach_msg_trailer_t* trailer) = 0;
+
+ //! \brief Handles exceptions raised by `exception_raise_state_identity()`
+ //! or `mach_exception_raise_state_identity()`.
+ //!
+ //! This behaves equivalently to a `catch_exception_raise_state_identity()`
+ //! function used with `exc_server()`, or a
+ //! `catch_mach_exception_raise_state_identity()` function used with
+ //! `mach_exc_server()`.
+ //!
+ //! \param[in] trailer The trailer received with the request message.
+ //! \param[out] destroy_request `true` if the request message is to be
+ //! destroyed even when this method returns success. See
+ //! MachMessageServer::Interface.
+ virtual kern_return_t CatchExceptionRaiseStateIdentity(
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const typename Traits::ExceptionCode* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_request) = 0;
+
+ protected:
+ ~Interface() {}
+ };
+
+ //! \brief Constructs an object of this class.
+ //!
+ //! \param[in] interface The interface to dispatch requests to. Weak.
+ explicit ExcServer(Interface* interface)
+ : MachMessageServer::Interface(), interface_(interface) {}
+
+ // MachMessageServer::Interface:
+
+ bool MachMessageServerFunction(const mach_msg_header_t* in_header,
+ mach_msg_header_t* out_header,
+ bool* destroy_complex_request) override;
+
+ std::set<mach_msg_id_t> MachMessageServerRequestIDs() override {
+ const mach_msg_id_t request_ids[] = {
+ Traits::kMachMessageIDExceptionRaise,
+ Traits::kMachMessageIDExceptionRaiseState,
+ Traits::kMachMessageIDExceptionRaiseStateIdentity,
+ };
+ return std::set<mach_msg_id_t>(&request_ids[0],
+ &request_ids[arraysize(request_ids)]);
+ }
+
+ mach_msg_size_t MachMessageServerRequestSize() override {
+ return sizeof(typename Traits::RequestUnion);
+ }
+
+ mach_msg_size_t MachMessageServerReplySize() override {
+ return sizeof(typename Traits::ReplyUnion);
+ }
+
+ private:
+ Interface* interface_; // weak
+
+ DISALLOW_COPY_AND_ASSIGN(ExcServer);
+};
+
+template <typename Traits>
+bool ExcServer<Traits>::MachMessageServerFunction(
+ const mach_msg_header_t* in_header,
+ mach_msg_header_t* out_header,
+ bool* destroy_complex_request) {
+ PrepareMIGReplyFromRequest(in_header, out_header);
+
+ const mach_msg_trailer_t* in_trailer =
+ MachMessageTrailerFromHeader(in_header);
+
+ switch (in_header->msgh_id) {
+ case Traits::kMachMessageIDExceptionRaise: {
+ // exception_raise(), catch_exception_raise(), mach_exception_raise(),
+ // catch_mach_exception_raise().
+ using Request = typename Traits::ExceptionRaiseRequest;
+ const Request* in_request = reinterpret_cast<const Request*>(in_header);
+ kern_return_t kr = Traits::MIGCheckRequestExceptionRaise(in_request);
+ if (kr != MACH_MSG_SUCCESS) {
+ SetMIGReplyError(out_header, kr);
+ return true;
+ }
+
+ using Reply = typename Traits::ExceptionRaiseReply;
+ Reply* out_reply = reinterpret_cast<Reply*>(out_header);
+ out_reply->RetCode =
+ interface_->CatchExceptionRaise(in_header->msgh_local_port,
+ in_request->thread.name,
+ in_request->task.name,
+ in_request->exception,
+ in_request->code,
+ in_request->codeCnt,
+ in_trailer,
+ destroy_complex_request);
+ if (out_reply->RetCode != KERN_SUCCESS) {
+ return true;
+ }
+
+ out_header->msgh_size = sizeof(*out_reply);
+ return true;
+ }
+
+ case Traits::kMachMessageIDExceptionRaiseState: {
+ // exception_raise_state(), catch_exception_raise_state(),
+ // mach_exception_raise_state(), catch_mach_exception_raise_state().
+ using Request = typename Traits::ExceptionRaiseStateRequest;
+ const Request* in_request = reinterpret_cast<const Request*>(in_header);
+
+ // in_request_1 is used for the portion of the request after the codes,
+ // which in theory can be variable-length. The check function will set it.
+ const Request* in_request_1;
+ kern_return_t kr =
+ Traits::MIGCheckRequestExceptionRaiseState(in_request, &in_request_1);
+ if (kr != MACH_MSG_SUCCESS) {
+ SetMIGReplyError(out_header, kr);
+ return true;
+ }
+
+ using Reply = typename Traits::ExceptionRaiseStateReply;
+ Reply* out_reply = reinterpret_cast<Reply*>(out_header);
+ out_reply->flavor = in_request_1->flavor;
+ out_reply->new_stateCnt = arraysize(out_reply->new_state);
+ out_reply->RetCode =
+ interface_->CatchExceptionRaiseState(in_header->msgh_local_port,
+ in_request->exception,
+ in_request->code,
+ in_request->codeCnt,
+ &out_reply->flavor,
+ in_request_1->old_state,
+ in_request_1->old_stateCnt,
+ out_reply->new_state,
+ &out_reply->new_stateCnt,
+ in_trailer);
+ if (out_reply->RetCode != KERN_SUCCESS) {
+ return true;
+ }
+
+ out_header->msgh_size =
+ sizeof(*out_reply) - sizeof(out_reply->new_state) +
+ sizeof(out_reply->new_state[0]) * out_reply->new_stateCnt;
+ return true;
+ }
+
+ case Traits::kMachMessageIDExceptionRaiseStateIdentity: {
+ // exception_raise_state_identity(),
+ // catch_exception_raise_state_identity(),
+ // mach_exception_raise_state_identity(),
+ // catch_mach_exception_raise_state_identity().
+ using Request = typename Traits::ExceptionRaiseStateIdentityRequest;
+ const Request* in_request = reinterpret_cast<const Request*>(in_header);
+
+ // in_request_1 is used for the portion of the request after the codes,
+ // which in theory can be variable-length. The check function will set it.
+ const Request* in_request_1;
+ kern_return_t kr = Traits::MIGCheckRequestExceptionRaiseStateIdentity(
+ in_request, &in_request_1);
+ if (kr != MACH_MSG_SUCCESS) {
+ SetMIGReplyError(out_header, kr);
+ return true;
+ }
+
+ using Reply = typename Traits::ExceptionRaiseStateIdentityReply;
+ Reply* out_reply = reinterpret_cast<Reply*>(out_header);
+ out_reply->flavor = in_request_1->flavor;
+ out_reply->new_stateCnt = arraysize(out_reply->new_state);
+ out_reply->RetCode = interface_->CatchExceptionRaiseStateIdentity(
+ in_header->msgh_local_port,
+ in_request->thread.name,
+ in_request->task.name,
+ in_request->exception,
+ in_request->code,
+ in_request->codeCnt,
+ &out_reply->flavor,
+ in_request_1->old_state,
+ in_request_1->old_stateCnt,
+ out_reply->new_state,
+ &out_reply->new_stateCnt,
+ in_trailer,
+ destroy_complex_request);
+ if (out_reply->RetCode != KERN_SUCCESS) {
+ return true;
+ }
+
+ out_header->msgh_size =
+ sizeof(*out_reply) - sizeof(out_reply->new_state) +
+ sizeof(out_reply->new_state[0]) * out_reply->new_stateCnt;
+ return true;
+ }
+
+ default: {
+ SetMIGReplyError(out_header, MIG_BAD_ID);
+ return false;
+ }
+ }
+}
+
+//! \brief A server interface for the `exc` or `mach_exc` Mach subsystems,
+//! simplified to have only a single interface method needing
+//! implementation.
+template <typename Traits>
+class SimplifiedExcServer final : public ExcServer<Traits>,
+ public ExcServer<Traits>::Interface {
+ public:
+ //! \brief An interface that the different request messages that are a part of
+ //! the `exc` or `mach_exc` Mach subsystems can be dispatched to.
+ class Interface {
+ public:
+ //! \brief Handles exceptions raised by `exception_raise()`,
+ //! `exception_raise_state()`, and `exception_raise_state_identity()`;
+ //! or `mach_exception_raise()`, `mach_exception_raise_state()`, and
+ //! `mach_exception_raise_state_identity()`.
+ //!
+ //! For convenience in implementation, these different “behaviors” of
+ //! exception messages are all mapped to a single interface method. The
+ //! exception’s original “behavior” is specified in the \a behavior
+ //! parameter. Only parameters that were supplied in the request message
+ //! are populated, other parameters are set to reasonable default values.
+ //!
+ //! The meanings of most parameters are identical to that of
+ //! ExcServer<>::Interface::CatchExceptionRaiseStateIdentity().
+ //!
+ //! \param[in] behavior `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`, or
+ //! `EXCEPTION_STATE_IDENTITY`, identifying which exception request
+ //! message was processed and thus which other parameters are valid.
+ //! When used with the `mach_exc` subsystem, `MACH_EXCEPTION_CODES` will
+ //! be ORed in to this parameter.
+ virtual kern_return_t CatchException(
+ exception_behavior_t behavior,
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const typename Traits::ExceptionCode* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_complex_request) = 0;
+
+ protected:
+ ~Interface() {}
+ };
+
+ //! \brief Constructs an object of this class.
+ //!
+ //! \param[in] interface The interface to dispatch requests to. Weak.
+ explicit SimplifiedExcServer(Interface* interface)
+ : ExcServer<Traits>(this),
+ ExcServer<Traits>::Interface(),
+ interface_(interface) {}
+
+ // ExcServer::Interface:
+
+ kern_return_t CatchExceptionRaise(exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const typename Traits::ExceptionCode* code,
+ mach_msg_type_number_t code_count,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_request) override {
+ thread_state_flavor_t flavor = THREAD_STATE_NONE;
+ mach_msg_type_number_t new_state_count = 0;
+ return interface_->CatchException(
+ Traits::kExceptionBehavior | EXCEPTION_DEFAULT,
+ exception_port,
+ thread,
+ task,
+ exception,
+ code_count ? code : nullptr,
+ code_count,
+ &flavor,
+ nullptr,
+ 0,
+ nullptr,
+ &new_state_count,
+ trailer,
+ destroy_request);
+ }
+
+ kern_return_t CatchExceptionRaiseState(
+ exception_handler_t exception_port,
+ exception_type_t exception,
+ const typename Traits::ExceptionCode* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count,
+ const mach_msg_trailer_t* trailer) override {
+ bool destroy_complex_request = false;
+ return interface_->CatchException(
+ Traits::kExceptionBehavior | EXCEPTION_STATE,
+ exception_port,
+ THREAD_NULL,
+ TASK_NULL,
+ exception,
+ code_count ? code : nullptr,
+ code_count,
+ flavor,
+ old_state_count ? old_state : nullptr,
+ old_state_count,
+ new_state_count ? new_state : nullptr,
+ new_state_count,
+ trailer,
+ &destroy_complex_request);
+ }
+
+ kern_return_t CatchExceptionRaiseStateIdentity(
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const typename Traits::ExceptionCode* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_request) override {
+ return interface_->CatchException(
+ Traits::kExceptionBehavior | EXCEPTION_STATE_IDENTITY,
+ exception_port,
+ thread,
+ task,
+ exception,
+ code_count ? code : nullptr,
+ code_count,
+ flavor,
+ old_state_count ? old_state : nullptr,
+ old_state_count,
+ new_state_count ? new_state : nullptr,
+ new_state_count,
+ trailer,
+ destroy_request);
+ }
+
+ private:
+ Interface* interface_; // weak
+
+ DISALLOW_COPY_AND_ASSIGN(SimplifiedExcServer);
+};
+
+} // namespace
+
+namespace internal {
+
+class UniversalMachExcServerImpl final
+ : public CompositeMachMessageServer,
+ public SimplifiedExcServer<ExcTraits>::Interface,
+ public SimplifiedExcServer<MachExcTraits>::Interface {
+ public:
+ explicit UniversalMachExcServerImpl(
+ UniversalMachExcServer::Interface* interface)
+ : CompositeMachMessageServer(),
+ SimplifiedExcServer<ExcTraits>::Interface(),
+ SimplifiedExcServer<MachExcTraits>::Interface(),
+ exc_server_(this),
+ mach_exc_server_(this),
+ interface_(interface) {
+ AddHandler(&exc_server_);
+ AddHandler(&mach_exc_server_);
+ }
+
+ ~UniversalMachExcServerImpl() {}
+
+ // SimplifiedExcServer<ExcTraits>::Interface:
+ kern_return_t CatchException(exception_behavior_t behavior,
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_complex_request) {
+ std::vector<mach_exception_data_type_t> mach_codes;
+ mach_codes.reserve(code_count);
+ for (size_t index = 0; index < code_count; ++index) {
+ mach_codes.push_back(code[index]);
+ }
+
+ return interface_->CatchMachException(behavior,
+ exception_port,
+ thread,
+ task,
+ exception,
+ code_count ? &mach_codes[0] : nullptr,
+ code_count,
+ flavor,
+ old_state_count ? old_state : nullptr,
+ old_state_count,
+ new_state_count ? new_state : nullptr,
+ new_state_count,
+ trailer,
+ destroy_complex_request);
+ }
+
+ // SimplifiedExcServer<MachExcTraits>::Interface:
+ kern_return_t CatchException(exception_behavior_t behavior,
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_complex_request) {
+ return interface_->CatchMachException(behavior,
+ exception_port,
+ thread,
+ task,
+ exception,
+ code_count ? code : nullptr,
+ code_count,
+ flavor,
+ old_state_count ? old_state : nullptr,
+ old_state_count,
+ new_state_count ? new_state : nullptr,
+ new_state_count,
+ trailer,
+ destroy_complex_request);
+ }
+
+ private:
+ SimplifiedExcServer<ExcTraits> exc_server_;
+ SimplifiedExcServer<MachExcTraits> mach_exc_server_;
+ UniversalMachExcServer::Interface* interface_; // weak
+
+ DISALLOW_COPY_AND_ASSIGN(UniversalMachExcServerImpl);
+};
+
+} // namespace internal
+
+UniversalMachExcServer::UniversalMachExcServer(
+ UniversalMachExcServer::Interface* interface)
+ : MachMessageServer::Interface(),
+ impl_(new internal::UniversalMachExcServerImpl(interface)) {
+}
+
+UniversalMachExcServer::~UniversalMachExcServer() {
+}
+
+bool UniversalMachExcServer::MachMessageServerFunction(
+ const mach_msg_header_t* in_header,
+ mach_msg_header_t* out_header,
+ bool* destroy_complex_request) {
+ return impl_->MachMessageServerFunction(
+ in_header, out_header, destroy_complex_request);
+}
+
+std::set<mach_msg_id_t> UniversalMachExcServer::MachMessageServerRequestIDs() {
+ return impl_->MachMessageServerRequestIDs();
+}
+
+mach_msg_size_t UniversalMachExcServer::MachMessageServerRequestSize() {
+ return impl_->MachMessageServerRequestSize();
+}
+
+mach_msg_size_t UniversalMachExcServer::MachMessageServerReplySize() {
+ return impl_->MachMessageServerReplySize();
+}
+
+kern_return_t ExcServerSuccessfulReturnValue(exception_behavior_t behavior,
+ bool set_thread_state) {
+ if (!set_thread_state && ExceptionBehaviorHasState(behavior)) {
+ return MACH_RCV_PORT_DIED;
+ }
+
+ return KERN_SUCCESS;
+}
+
+void ExcServerCopyState(exception_behavior_t behavior,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count) {
+ if (ExceptionBehaviorHasState(behavior)) {
+ *new_state_count = std::min(old_state_count, *new_state_count);
+ memcpy(new_state, old_state, *new_state_count * sizeof(old_state[0]));
+ }
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/exc_server_variants.h b/chromium/third_party/crashpad/crashpad/util/mach/exc_server_variants.h
new file mode 100644
index 00000000000..aaac36ad75f
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/exc_server_variants.h
@@ -0,0 +1,208 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MACH_EXC_SERVER_VARIANTS_H_
+#define CRASHPAD_UTIL_MACH_EXC_SERVER_VARIANTS_H_
+
+#include <mach/mach.h>
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "util/mach/mach_extensions.h"
+#include "util/mach/mach_message_server.h"
+
+namespace crashpad {
+
+namespace internal {
+class UniversalMachExcServerImpl;
+} // namespace internal
+
+//! \brief A server interface for the `exc` and `mach_exc` Mach subsystems,
+//! unified to handle exceptions delivered to either subsystem, and
+//! simplified to have only a single interface method needing
+//! implementation.
+//!
+//! The `<mach/exc.defs>` and `<mach/mach_exc.defs>` interfaces are identical,
+//! except that the latter allows for 64-bit exception codes, and is requested
+//! by setting the MACH_EXCEPTION_CODES behavior bit associated with an
+//! exception port.
+//!
+//! UniversalMachExcServer operates by translating messages received in the
+//! `exc` subsystem to a variant that is compatible with the `mach_exc`
+//! subsystem. This involves changing the format of \a code, the exception code
+//! field, from `exception_data_type_t` to `mach_exception_data_type_t`.
+class UniversalMachExcServer final : public MachMessageServer::Interface {
+ public:
+ //! \brief An interface that the different request messages that are a part of
+ //! the `exc` and `mach_exc` Mach subsystems can be dispatched to.
+ class Interface {
+ public:
+ //! \brief Handles exceptions raised by `exception_raise()`,
+ //! `exception_raise_state()`, `exception_raise_state_identity()`,
+ //! `mach_exception_raise()`, `mach_exception_raise_state()`, and
+ //! `mach_exception_raise_state_identity()`.
+ //!
+ //! For convenience in implementation, these different “behaviors” of
+ //! exception messages are all mapped to a single interface method. The
+ //! exception’s original “behavior” is specified in the \a behavior
+ //! parameter. Only parameters that were supplied in the request message
+ //! are populated, other parameters are set to reasonable default values.
+ //!
+ //! This behaves equivalently to a `catch_exception_raise_state_identity()`
+ //! function used with `exc_server()`, or a
+ //! `catch_mach_exception_raise_state_identity()` function used with
+ //! `mach_exc_server()`. The meanings of most parameters are identical to
+ //! their meanings to these functions.
+ //!
+ //! \param[in] behavior `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`,
+ //! or `EXCEPTION_STATE_IDENTITY`, possibly with `MACH_EXCEPTION_CODES`
+ //! ORed in. This identifies which exception request message was
+ //! processed and thus which other parameters are valid.
+ //! \param[in] trailer The trailer received with the request message.
+ //! \param[out] destroy_request `true` if the request message is to be
+ //! destroyed even when this method returns success. See
+ //! MachMessageServer::Interface.
+ virtual kern_return_t CatchMachException(
+ exception_behavior_t behavior,
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_complex_request) = 0;
+
+ protected:
+ ~Interface() {}
+ };
+
+ //! \brief Constructs an object of this class.
+ //!
+ //! \param[in] interface The interface to dispatch requests to. Weak.
+ explicit UniversalMachExcServer(Interface* interface);
+
+ ~UniversalMachExcServer();
+
+ // MachMessageServer::Interface:
+ bool MachMessageServerFunction(const mach_msg_header_t* in_header,
+ mach_msg_header_t* out_header,
+ bool* destroy_complex_request) override;
+ std::set<mach_msg_id_t> MachMessageServerRequestIDs() override;
+ mach_msg_size_t MachMessageServerRequestSize() override;
+ mach_msg_size_t MachMessageServerReplySize() override;
+
+ private:
+ scoped_ptr<internal::UniversalMachExcServerImpl> impl_;
+
+ DISALLOW_COPY_AND_ASSIGN(UniversalMachExcServer);
+};
+
+//! \brief Computes an approriate successful return value for an exception
+//! handler function.
+//!
+//! For exception handlers that respond to state-carrying behaviors, when the
+//! handler is called by the kernel (as it is normally), the kernel will attempt
+//! to set a new thread state when the exception handler returns successfully.
+//! Other code that mimics the kernel’s exception-delivery semantics may
+//! implement the same or similar behavior. In some situations, it is
+//! undesirable to set a new thread state. If the exception handler were to
+//! return unsuccessfully, however, the kernel would continue searching for an
+//! exception handler at a wider (task or host) scope. This may also be
+//! undesirable.
+//!
+//! If such exception handlers return `MACH_RCV_PORT_DIED`, the kernel will not
+//! set a new thread state and will also not search for another exception
+//! handler. See 10.9.4 `xnu-2422.110.17/osfmk/kern/exception.c`.
+//! `exception_deliver()` will only set a new thread state if the handler’s
+//! return code was `MACH_MSG_SUCCESS` (a synonym for `KERN_SUCCESS`), and
+//! subsequently, `exception_triage()` will not search for a new handler if the
+//! handler’s return code was `KERN_SUCCESS` or `MACH_RCV_PORT_DIED`.
+//!
+//! This function allows exception handlers to compute an appropriate return
+//! code to influence their caller (the kernel) in the desired way with respect
+//! to setting a new thread state while suppressing the caller’s subsequent
+//! search for other exception handlers. An exception handler should return the
+//! value returned by this function.
+//!
+//! This function is useful even for `EXC_CRASH` handlers, where returning
+//! `KERN_SUCCESS` and allowing the kernel to set a new thread state has been
+//! observed to cause a perceptible and unnecessary waste of time. The victim
+//! task in an `EXC_CRASH` handler is already being terminated and is no longer
+//! schedulable, so there is no point in setting the states of any of its
+//! threads.
+//!
+//! \param[in] behavior The behavior of the exception handler as invoked. This
+//! may be taken directly from the \a behavior parameter of
+//! internal::SimplifiedExcServer::Interface::CatchException(), for example.
+//! \param[in] set_thread_state `true` if the handler would like its caller to
+//! set the new thread state using the \a flavor, \a new_state, and \a
+//! new_state_count out parameters. This can only happen when \a behavior is
+//! a state-carrying behavior.
+//!
+//! \return `KERN_SUCCESS` or `MACH_RCV_PORT_DIED`. `KERN_SUCCESS` is used when
+//! \a behavior is not a state-carrying behavior, or when it is a
+//! state-carrying behavior and \a set_thread_state is `true`.
+//! `MACH_RCV_PORT_DIED` is used when \a behavior is a state-carrying
+//! behavior and \a set_thread_state is `false`.
+kern_return_t ExcServerSuccessfulReturnValue(exception_behavior_t behavior,
+ bool set_thread_state);
+
+//! \brief Copies the old state to the new state for state-carrying exceptions.
+//!
+//! When the kernel sends a state-carrying exception request and the response is
+//! successful (`MACH_MSG_SUCCESS`, a synonym for `KERN_SUCCESS`), it will set
+//! a new thread state based on \a new_state and \a new_state_count. To ease
+//! initialization of the new state, this function copies \a old_state and
+//! \a old_state_count. This is only done if \a behavior indicates a
+//! state-carrying exception.
+//!
+//! \param[in] behavior The behavior of the exception handler as invoked. This
+//! may be taken directly from the \a behavior parameter of
+//! internal::SimplifiedExcServer::Interface::CatchException(), for example.
+//! \param[in] old_state The original state value. This may be taken directly
+//! from the \a old_state parameter of
+//! internal::SimplifiedExcServer::Interface::CatchException(), for example.
+//! \param[in] old_state_count The number of significant `natural_t` words in \a
+//! old_state. This may be taken directly from the \a old_state_count
+//! parameter of internal::SimplifiedExcServer::Interface::CatchException(),
+//! for example.
+//! \param[out] new_state The state value to be set. This may be taken directly
+//! from the \a new_state parameter of
+//! internal::SimplifiedExcServer::Interface::CatchException(), for example.
+//! This parameter is untouched if \a behavior is not state-carrying.
+//! \param[inout] new_state_count On entry, the number of `natural_t` words
+//! available to be written to in \a new_state. On return, the number of
+//! significant `natural_t` words in \a new_state. This may be taken
+//! directly from the \a new_state_count parameter of
+//! internal::SimplifiedExcServer::Interface::CatchException(), for example.
+//! This parameter is untouched if \a behavior is not state-carrying. If \a
+//! \a behavior is state-carrying, this parameter should be at least as
+//! large as \a old_state_count.
+void ExcServerCopyState(exception_behavior_t behavior,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count);
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MACH_EXC_SERVER_VARIANTS_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/exc_server_variants_test.cc b/chromium/third_party/crashpad/crashpad/util/mach/exc_server_variants_test.cc
new file mode 100644
index 00000000000..a9a300003c3
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/exc_server_variants_test.cc
@@ -0,0 +1,1301 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/exc_server_variants.h"
+
+#include <mach/mach.h>
+#include <string.h>
+
+#include "base/strings/stringprintf.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "test/mac/mach_errors.h"
+#include "test/mac/mach_multiprocess.h"
+#include "util/mach/exception_behaviors.h"
+#include "util/mach/exception_types.h"
+#include "util/mach/mach_message.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+using testing::DefaultValue;
+using testing::Eq;
+using testing::Pointee;
+using testing::Return;
+
+// Fake Mach ports. These aren’t used as ports in these tests, they’re just used
+// as cookies to make sure that the correct values get passed to the correct
+// places.
+const mach_port_t kClientRemotePort = 0x01010101;
+const mach_port_t kServerLocalPort = 0x02020202;
+const thread_t kExceptionThreadPort = 0x03030303;
+const task_t kExceptionTaskPort = 0x04040404;
+
+// Other fake exception values.
+const exception_type_t kExceptionType = EXC_BAD_ACCESS;
+
+// Test using an exception code with the high bit set to ensure that it gets
+// promoted to the wider mach_exception_data_type_t type as a signed quantity.
+const exception_data_type_t kTestExceptonCodes[] = {
+ KERN_PROTECTION_FAILURE,
+ implicit_cast<exception_data_type_t>(0xfedcba98),
+};
+
+const mach_exception_data_type_t kTestMachExceptionCodes[] = {
+ KERN_PROTECTION_FAILURE,
+ implicit_cast<mach_exception_data_type_t>(0xfedcba9876543210),
+};
+
+const thread_state_flavor_t kThreadStateFlavor = MACHINE_THREAD_STATE;
+const mach_msg_type_number_t kThreadStateFlavorCount =
+ MACHINE_THREAD_STATE_COUNT;
+
+void InitializeMachMsgPortDescriptor(mach_msg_port_descriptor_t* descriptor,
+ mach_port_t port) {
+ descriptor->name = port;
+ descriptor->disposition = MACH_MSG_TYPE_PORT_SEND;
+ descriptor->type = MACH_MSG_PORT_DESCRIPTOR;
+}
+
+// The definitions of the request and reply structures from mach_exc.h aren’t
+// available here. They need custom initialization code, and the reply
+// structures need verification code too, so duplicate the expected definitions
+// of the structures from both exc.h and mach_exc.h here in this file, and
+// provide the initialization and verification code as methods in true
+// object-oriented fashion.
+
+struct __attribute__((packed, aligned(4))) ExceptionRaiseRequest {
+ ExceptionRaiseRequest() {
+ memset(this, 0xa5, sizeof(*this));
+ Head.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND) |
+ MACH_MSGH_BITS_COMPLEX;
+ Head.msgh_size = sizeof(*this) - sizeof(trailer);
+ Head.msgh_remote_port = kClientRemotePort;
+ Head.msgh_local_port = kServerLocalPort;
+ Head.msgh_id = 2401;
+ msgh_body.msgh_descriptor_count = 2;
+ InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort);
+ InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort);
+ NDR = NDR_record;
+ exception = kExceptionType;
+ codeCnt = 2;
+ code[0] = kTestExceptonCodes[0];
+ code[1] = kTestExceptonCodes[1];
+ }
+
+ mach_msg_header_t Head;
+ mach_msg_body_t msgh_body;
+ mach_msg_port_descriptor_t thread;
+ mach_msg_port_descriptor_t task;
+ NDR_record_t NDR;
+ exception_type_t exception;
+ mach_msg_type_number_t codeCnt;
+ integer_t code[2];
+ mach_msg_trailer_t trailer;
+};
+
+struct __attribute__((packed, aligned(4))) ExceptionRaiseReply {
+ ExceptionRaiseReply() {
+ memset(this, 0x5a, sizeof(*this));
+ RetCode = KERN_FAILURE;
+ }
+
+ // Verify accepts a |behavior| parameter because the same message format and
+ // verification function is used for ExceptionRaiseReply and
+ // MachExceptionRaiseReply. Knowing which behavior is expected allows the
+ // message ID to be checked.
+ void Verify(exception_behavior_t behavior) {
+ EXPECT_EQ(implicit_cast<mach_msg_bits_t>(
+ MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0)),
+ Head.msgh_bits);
+ EXPECT_EQ(sizeof(*this), Head.msgh_size);
+ EXPECT_EQ(kClientRemotePort, Head.msgh_remote_port);
+ EXPECT_EQ(kMachPortNull, Head.msgh_local_port);
+ switch (behavior) {
+ case EXCEPTION_DEFAULT:
+ EXPECT_EQ(2501, Head.msgh_id);
+ break;
+ case EXCEPTION_DEFAULT | kMachExceptionCodes:
+ EXPECT_EQ(2505, Head.msgh_id);
+ break;
+ default:
+ ADD_FAILURE() << "behavior " << behavior << ", Head.msgh_id "
+ << Head.msgh_id;
+ break;
+ }
+ EXPECT_EQ(0, memcmp(&NDR, &NDR_record, sizeof(NDR)));
+ EXPECT_EQ(KERN_SUCCESS, RetCode);
+ }
+
+ mach_msg_header_t Head;
+ NDR_record_t NDR;
+ kern_return_t RetCode;
+};
+
+struct __attribute__((packed, aligned(4))) ExceptionRaiseStateRequest {
+ ExceptionRaiseStateRequest() {
+ memset(this, 0xa5, sizeof(*this));
+ Head.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND);
+ Head.msgh_size = sizeof(*this) - sizeof(trailer);
+ Head.msgh_remote_port = kClientRemotePort;
+ Head.msgh_local_port = kServerLocalPort;
+ Head.msgh_id = 2402;
+ NDR = NDR_record;
+ exception = kExceptionType;
+ codeCnt = 2;
+ code[0] = kTestExceptonCodes[0];
+ code[1] = kTestExceptonCodes[1];
+ flavor = kThreadStateFlavor;
+ old_stateCnt = kThreadStateFlavorCount;
+
+ // Adjust the message size for the data that it’s actually carrying, which
+ // may be smaller than the maximum that it can carry.
+ Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state);
+ }
+
+ // Because the message size has been adjusted, the trailer may not appear in
+ // its home member variable. This computes the actual address of the trailer.
+ const mach_msg_trailer_t* Trailer() const {
+ return MachMessageTrailerFromHeader(&Head);
+ }
+
+ mach_msg_header_t Head;
+ NDR_record_t NDR;
+ exception_type_t exception;
+ mach_msg_type_number_t codeCnt;
+ integer_t code[2];
+ int flavor;
+ mach_msg_type_number_t old_stateCnt;
+ natural_t old_state[THREAD_STATE_MAX];
+ mach_msg_trailer_t trailer;
+};
+
+struct __attribute__((packed, aligned(4))) ExceptionRaiseStateReply {
+ ExceptionRaiseStateReply() {
+ memset(this, 0x5a, sizeof(*this));
+ RetCode = KERN_FAILURE;
+ }
+
+ // Verify accepts a |behavior| parameter because the same message format and
+ // verification function is used for ExceptionRaiseStateReply,
+ // ExceptionRaiseStateIdentityReply, MachExceptionRaiseStateReply, and
+ // MachExceptionRaiseStateIdentityReply. Knowing which behavior is expected
+ // allows the message ID to be checked.
+ void Verify(exception_behavior_t behavior) {
+ EXPECT_EQ(implicit_cast<mach_msg_bits_t>(
+ MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0)),
+ Head.msgh_bits);
+ EXPECT_EQ(sizeof(*this), Head.msgh_size);
+ EXPECT_EQ(kClientRemotePort, Head.msgh_remote_port);
+ EXPECT_EQ(kMachPortNull, Head.msgh_local_port);
+ switch (behavior) {
+ case EXCEPTION_STATE:
+ EXPECT_EQ(2502, Head.msgh_id);
+ break;
+ case EXCEPTION_STATE_IDENTITY:
+ EXPECT_EQ(2503, Head.msgh_id);
+ break;
+ case EXCEPTION_STATE | kMachExceptionCodes:
+ EXPECT_EQ(2506, Head.msgh_id);
+ break;
+ case EXCEPTION_STATE_IDENTITY | kMachExceptionCodes:
+ EXPECT_EQ(2507, Head.msgh_id);
+ break;
+ default:
+ ADD_FAILURE() << "behavior " << behavior << ", Head.msgh_id "
+ << Head.msgh_id;
+ break;
+ }
+ EXPECT_EQ(0, memcmp(&NDR, &NDR_record, sizeof(NDR)));
+ EXPECT_EQ(KERN_SUCCESS, RetCode);
+ EXPECT_EQ(kThreadStateFlavor, flavor);
+ EXPECT_EQ(arraysize(new_state), new_stateCnt);
+ }
+
+ mach_msg_header_t Head;
+ NDR_record_t NDR;
+ kern_return_t RetCode;
+ int flavor;
+ mach_msg_type_number_t new_stateCnt;
+ natural_t new_state[THREAD_STATE_MAX];
+};
+
+struct __attribute__((packed, aligned(4))) ExceptionRaiseStateIdentityRequest {
+ ExceptionRaiseStateIdentityRequest() {
+ memset(this, 0xa5, sizeof(*this));
+ Head.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND) |
+ MACH_MSGH_BITS_COMPLEX;
+ Head.msgh_size = sizeof(*this) - sizeof(trailer);
+ Head.msgh_remote_port = kClientRemotePort;
+ Head.msgh_local_port = kServerLocalPort;
+ Head.msgh_id = 2403;
+ msgh_body.msgh_descriptor_count = 2;
+ InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort);
+ InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort);
+ NDR = NDR_record;
+ exception = kExceptionType;
+ codeCnt = 2;
+ code[0] = kTestExceptonCodes[0];
+ code[1] = kTestExceptonCodes[1];
+ flavor = kThreadStateFlavor;
+ old_stateCnt = kThreadStateFlavorCount;
+
+ // Adjust the message size for the data that it’s actually carrying, which
+ // may be smaller than the maximum that it can carry.
+ Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state);
+ }
+
+ // Because the message size has been adjusted, the trailer may not appear in
+ // its home member variable. This computes the actual address of the trailer.
+ const mach_msg_trailer_t* Trailer() const {
+ return MachMessageTrailerFromHeader(&Head);
+ }
+
+ mach_msg_header_t Head;
+ mach_msg_body_t msgh_body;
+ mach_msg_port_descriptor_t thread;
+ mach_msg_port_descriptor_t task;
+ NDR_record_t NDR;
+ exception_type_t exception;
+ mach_msg_type_number_t codeCnt;
+ integer_t code[2];
+ int flavor;
+ mach_msg_type_number_t old_stateCnt;
+ natural_t old_state[THREAD_STATE_MAX];
+ mach_msg_trailer_t trailer;
+};
+
+// The reply messages for exception_raise_state and
+// exception_raise_state_identity are identical.
+using ExceptionRaiseStateIdentityReply = ExceptionRaiseStateReply;
+
+struct __attribute__((packed, aligned(4))) MachExceptionRaiseRequest {
+ MachExceptionRaiseRequest() {
+ memset(this, 0xa5, sizeof(*this));
+ Head.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND) |
+ MACH_MSGH_BITS_COMPLEX;
+ Head.msgh_size = sizeof(*this) - sizeof(trailer);
+ Head.msgh_remote_port = kClientRemotePort;
+ Head.msgh_local_port = kServerLocalPort;
+ Head.msgh_id = 2405;
+ msgh_body.msgh_descriptor_count = 2;
+ InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort);
+ InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort);
+ NDR = NDR_record;
+ exception = kExceptionType;
+ codeCnt = 2;
+ code[0] = kTestMachExceptionCodes[0];
+ code[1] = kTestMachExceptionCodes[1];
+ }
+
+ mach_msg_header_t Head;
+ mach_msg_body_t msgh_body;
+ mach_msg_port_descriptor_t thread;
+ mach_msg_port_descriptor_t task;
+ NDR_record_t NDR;
+ exception_type_t exception;
+ mach_msg_type_number_t codeCnt;
+ int64_t code[2];
+ mach_msg_trailer_t trailer;
+};
+
+// The reply messages for exception_raise and mach_exception_raise are
+// identical.
+using MachExceptionRaiseReply = ExceptionRaiseReply;
+
+struct __attribute__((packed, aligned(4))) MachExceptionRaiseStateRequest {
+ MachExceptionRaiseStateRequest() {
+ memset(this, 0xa5, sizeof(*this));
+ Head.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND);
+ Head.msgh_size = sizeof(*this) - sizeof(trailer);
+ Head.msgh_remote_port = kClientRemotePort;
+ Head.msgh_local_port = kServerLocalPort;
+ Head.msgh_id = 2406;
+ NDR = NDR_record;
+ exception = kExceptionType;
+ codeCnt = 2;
+ code[0] = kTestMachExceptionCodes[0];
+ code[1] = kTestMachExceptionCodes[1];
+ flavor = kThreadStateFlavor;
+ old_stateCnt = kThreadStateFlavorCount;
+
+ // Adjust the message size for the data that it’s actually carrying, which
+ // may be smaller than the maximum that it can carry.
+ Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state);
+ }
+
+ // Because the message size has been adjusted, the trailer may not appear in
+ // its home member variable. This computes the actual address of the trailer.
+ const mach_msg_trailer_t* Trailer() const {
+ return MachMessageTrailerFromHeader(&Head);
+ }
+
+ mach_msg_header_t Head;
+ NDR_record_t NDR;
+ exception_type_t exception;
+ mach_msg_type_number_t codeCnt;
+ int64_t code[2];
+ int flavor;
+ mach_msg_type_number_t old_stateCnt;
+ natural_t old_state[THREAD_STATE_MAX];
+ mach_msg_trailer_t trailer;
+};
+
+// The reply messages for exception_raise_state and mach_exception_raise_state
+// are identical.
+using MachExceptionRaiseStateReply = ExceptionRaiseStateReply;
+
+struct __attribute__((packed,
+ aligned(4))) MachExceptionRaiseStateIdentityRequest {
+ MachExceptionRaiseStateIdentityRequest() {
+ memset(this, 0xa5, sizeof(*this));
+ Head.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND) |
+ MACH_MSGH_BITS_COMPLEX;
+ Head.msgh_size = sizeof(*this) - sizeof(trailer);
+ Head.msgh_remote_port = kClientRemotePort;
+ Head.msgh_local_port = kServerLocalPort;
+ Head.msgh_id = 2407;
+ msgh_body.msgh_descriptor_count = 2;
+ InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort);
+ InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort);
+ NDR = NDR_record;
+ exception = kExceptionType;
+ codeCnt = 2;
+ code[0] = kTestMachExceptionCodes[0];
+ code[1] = kTestMachExceptionCodes[1];
+ flavor = kThreadStateFlavor;
+ old_stateCnt = kThreadStateFlavorCount;
+
+ // Adjust the message size for the data that it’s actually carrying, which
+ // may be smaller than the maximum that it can carry.
+ Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state);
+ }
+
+ // Because the message size has been adjusted, the trailer may not appear in
+ // its home member variable. This computes the actual address of the trailer.
+ const mach_msg_trailer_t* Trailer() const {
+ return MachMessageTrailerFromHeader(&Head);
+ }
+
+ mach_msg_header_t Head;
+ mach_msg_body_t msgh_body;
+ mach_msg_port_descriptor_t thread;
+ mach_msg_port_descriptor_t task;
+ NDR_record_t NDR;
+ exception_type_t exception;
+ mach_msg_type_number_t codeCnt;
+ int64_t code[2];
+ int flavor;
+ mach_msg_type_number_t old_stateCnt;
+ natural_t old_state[THREAD_STATE_MAX];
+ mach_msg_trailer_t trailer;
+};
+
+// The reply messages for exception_raise_state_identity and
+// mach_exception_raise_state_identity are identical.
+using MachExceptionRaiseStateIdentityReply = ExceptionRaiseStateIdentityReply;
+
+// InvalidRequest and BadIDErrorReply are used to test that
+// UniversalMachExcServer deals appropriately with messages that it does not
+// understand: messages with an unknown Head.msgh_id.
+
+struct InvalidRequest : public mach_msg_empty_send_t {
+ explicit InvalidRequest(mach_msg_id_t id) {
+ memset(this, 0xa5, sizeof(*this));
+ header.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND);
+ header.msgh_size = sizeof(*this);
+ header.msgh_remote_port = kClientRemotePort;
+ header.msgh_local_port = kServerLocalPort;
+ header.msgh_id = id;
+ }
+};
+
+struct BadIDErrorReply : public mig_reply_error_t {
+ BadIDErrorReply() {
+ memset(this, 0x5a, sizeof(*this));
+ RetCode = KERN_FAILURE;
+ }
+
+ void Verify(mach_msg_id_t id) {
+ EXPECT_EQ(implicit_cast<mach_msg_bits_t>(
+ MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0)),
+ Head.msgh_bits);
+ EXPECT_EQ(sizeof(*this), Head.msgh_size);
+ EXPECT_EQ(kClientRemotePort, Head.msgh_remote_port);
+ EXPECT_EQ(kMachPortNull, Head.msgh_local_port);
+ EXPECT_EQ(id + 100, Head.msgh_id);
+ EXPECT_EQ(0, memcmp(&NDR, &NDR_record, sizeof(NDR)));
+ EXPECT_EQ(MIG_BAD_ID, RetCode);
+ }
+};
+
+class MockUniversalMachExcServer : public UniversalMachExcServer::Interface {
+ public:
+ struct ConstExceptionCodes {
+ const mach_exception_data_type_t* code;
+ mach_msg_type_number_t code_count;
+ };
+ struct ThreadStateAndCount {
+ thread_state_t state;
+ mach_msg_type_number_t* state_count;
+ };
+ struct ConstThreadStateAndCount {
+ ConstThreadState state;
+ mach_msg_type_number_t* state_count;
+ };
+
+ // UniversalMachExcServer::Interface:
+
+ // CatchMachException is the method to mock, but it has 13 parameters, and
+ // gmock can only mock methods with up to 10 parameters. Coalesce some related
+ // parameters together into structs, and call a mocked method.
+ virtual kern_return_t CatchMachException(
+ exception_behavior_t behavior,
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_complex_request) override {
+ *destroy_complex_request = true;
+ const ConstExceptionCodes exception_codes = {code, code_count};
+ const ConstThreadStateAndCount old_thread_state = {old_state,
+ &old_state_count};
+ ThreadStateAndCount new_thread_state = {new_state, new_state_count};
+ return MockCatchMachException(behavior,
+ exception_port,
+ thread,
+ task,
+ exception,
+ &exception_codes,
+ flavor,
+ &old_thread_state,
+ &new_thread_state,
+ trailer);
+ }
+
+ MOCK_METHOD10(MockCatchMachException,
+ kern_return_t(exception_behavior_t behavior,
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const ConstExceptionCodes* exception_codes,
+ thread_state_flavor_t* flavor,
+ const ConstThreadStateAndCount* old_thread_state,
+ ThreadStateAndCount* new_thread_state,
+ const mach_msg_trailer_t* trailer));
+};
+
+// Matcher for ConstExceptionCodes, testing that it carries 2 codes matching
+// code_0 and code_1.
+MATCHER_P2(AreExceptionCodes, code_0, code_1, "") {
+ if (!arg) {
+ return false;
+ }
+
+ if (arg->code_count == 2 && arg->code[0] == code_0 &&
+ arg->code[1] == code_1) {
+ return true;
+ }
+
+ *result_listener << "codes (";
+ for (size_t index = 0; index < arg->code_count; ++index) {
+ *result_listener << arg->code[index];
+ if (index < arg->code_count - 1) {
+ *result_listener << ", ";
+ }
+ }
+ *result_listener << ")";
+
+ return false;
+}
+
+// Matcher for ThreadStateAndCount and ConstThreadStateAndCount, testing that
+// *state_count is present and matches the specified value. If 0 is specified
+// for the count, |state| must be nullptr (not present), otherwise |state| must
+// not be nullptr (present).
+MATCHER_P(IsThreadStateAndCount, state_count, "") {
+ if (!arg) {
+ return false;
+ }
+ if (!arg->state_count) {
+ *result_listener << "state_count nullptr";
+ return false;
+ }
+ if (*(arg->state_count) != state_count) {
+ *result_listener << "*state_count " << *(arg->state_count);
+ return false;
+ }
+ if (state_count) {
+ if (!arg->state) {
+ *result_listener << "*state_count " << state_count << ", state nullptr";
+ return false;
+ }
+ } else {
+ if (arg->state) {
+ *result_listener << "*state_count 0, state non-nullptr (" << arg->state
+ << ")";
+ return false;
+ }
+ }
+ return true;
+}
+
+template <typename T>
+class ScopedDefaultValue {
+ public:
+ explicit ScopedDefaultValue(const T& default_value) {
+ DefaultValue<T>::Set(default_value);
+ }
+
+ ~ScopedDefaultValue() { DefaultValue<T>::Clear(); }
+};
+
+TEST(ExcServerVariants, MockExceptionRaise) {
+ ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE);
+
+ MockUniversalMachExcServer server;
+ UniversalMachExcServer universal_mach_exc_server(&server);
+
+ std::set<mach_msg_id_t> ids =
+ universal_mach_exc_server.MachMessageServerRequestIDs();
+ EXPECT_NE(ids.end(), ids.find(2401)); // There is no constant for this.
+
+ ExceptionRaiseRequest request;
+ EXPECT_LE(request.Head.msgh_size,
+ universal_mach_exc_server.MachMessageServerRequestSize());
+
+ ExceptionRaiseReply reply;
+ EXPECT_LE(sizeof(reply),
+ universal_mach_exc_server.MachMessageServerReplySize());
+
+ const exception_behavior_t kExceptionBehavior = EXCEPTION_DEFAULT;
+
+ EXPECT_CALL(server,
+ MockCatchMachException(kExceptionBehavior,
+ kServerLocalPort,
+ kExceptionThreadPort,
+ kExceptionTaskPort,
+ kExceptionType,
+ AreExceptionCodes(kTestExceptonCodes[0],
+ kTestExceptonCodes[1]),
+ Pointee(Eq(THREAD_STATE_NONE)),
+ IsThreadStateAndCount(0u),
+ IsThreadStateAndCount(0u),
+ Eq(&request.trailer)))
+ .WillOnce(Return(KERN_SUCCESS))
+ .RetiresOnSaturation();
+
+ bool destroy_complex_request = false;
+ EXPECT_TRUE(universal_mach_exc_server.MachMessageServerFunction(
+ reinterpret_cast<mach_msg_header_t*>(&request),
+ reinterpret_cast<mach_msg_header_t*>(&reply),
+ &destroy_complex_request));
+ EXPECT_TRUE(destroy_complex_request);
+
+ reply.Verify(kExceptionBehavior);
+}
+
+TEST(ExcServerVariants, MockExceptionRaiseState) {
+ ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE);
+
+ MockUniversalMachExcServer server;
+ UniversalMachExcServer universal_mach_exc_server(&server);
+
+ std::set<mach_msg_id_t> ids =
+ universal_mach_exc_server.MachMessageServerRequestIDs();
+ EXPECT_NE(ids.end(), ids.find(2402)); // There is no constant for this.
+
+ ExceptionRaiseStateRequest request;
+ EXPECT_LE(request.Head.msgh_size,
+ universal_mach_exc_server.MachMessageServerRequestSize());
+
+ ExceptionRaiseStateReply reply;
+ EXPECT_LE(sizeof(reply),
+ universal_mach_exc_server.MachMessageServerReplySize());
+
+ const exception_behavior_t kExceptionBehavior = EXCEPTION_STATE;
+
+ EXPECT_CALL(
+ server,
+ MockCatchMachException(
+ kExceptionBehavior,
+ kServerLocalPort,
+ THREAD_NULL,
+ TASK_NULL,
+ kExceptionType,
+ AreExceptionCodes(kTestExceptonCodes[0], kTestExceptonCodes[1]),
+ Pointee(Eq(kThreadStateFlavor)),
+ IsThreadStateAndCount(kThreadStateFlavorCount),
+ IsThreadStateAndCount(arraysize(reply.new_state)),
+ Eq(request.Trailer())))
+ .WillOnce(Return(KERN_SUCCESS))
+ .RetiresOnSaturation();
+
+ bool destroy_complex_request = false;
+ EXPECT_TRUE(universal_mach_exc_server.MachMessageServerFunction(
+ reinterpret_cast<mach_msg_header_t*>(&request),
+ reinterpret_cast<mach_msg_header_t*>(&reply),
+ &destroy_complex_request));
+
+ // The request wasn’t complex, so nothing got a chance to change the value of
+ // this variable.
+ EXPECT_FALSE(destroy_complex_request);
+
+ reply.Verify(kExceptionBehavior);
+}
+
+TEST(ExcServerVariants, MockExceptionRaiseStateIdentity) {
+ ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE);
+
+ MockUniversalMachExcServer server;
+ UniversalMachExcServer universal_mach_exc_server(&server);
+
+ std::set<mach_msg_id_t> ids =
+ universal_mach_exc_server.MachMessageServerRequestIDs();
+ EXPECT_NE(ids.end(), ids.find(2403)); // There is no constant for this.
+
+ ExceptionRaiseStateIdentityRequest request;
+ EXPECT_LE(request.Head.msgh_size,
+ universal_mach_exc_server.MachMessageServerRequestSize());
+
+ ExceptionRaiseStateIdentityReply reply;
+ EXPECT_LE(sizeof(reply),
+ universal_mach_exc_server.MachMessageServerReplySize());
+
+ const exception_behavior_t kExceptionBehavior = EXCEPTION_STATE_IDENTITY;
+
+ EXPECT_CALL(
+ server,
+ MockCatchMachException(
+ kExceptionBehavior,
+ kServerLocalPort,
+ kExceptionThreadPort,
+ kExceptionTaskPort,
+ kExceptionType,
+ AreExceptionCodes(kTestExceptonCodes[0], kTestExceptonCodes[1]),
+ Pointee(Eq(kThreadStateFlavor)),
+ IsThreadStateAndCount(kThreadStateFlavorCount),
+ IsThreadStateAndCount(arraysize(reply.new_state)),
+ Eq(request.Trailer())))
+ .WillOnce(Return(KERN_SUCCESS))
+ .RetiresOnSaturation();
+
+ bool destroy_complex_request = false;
+ EXPECT_TRUE(universal_mach_exc_server.MachMessageServerFunction(
+ reinterpret_cast<mach_msg_header_t*>(&request),
+ reinterpret_cast<mach_msg_header_t*>(&reply),
+ &destroy_complex_request));
+ EXPECT_TRUE(destroy_complex_request);
+
+ reply.Verify(kExceptionBehavior);
+}
+
+TEST(ExcServerVariants, MockMachExceptionRaise) {
+ ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE);
+
+ MockUniversalMachExcServer server;
+ UniversalMachExcServer universal_mach_exc_server(&server);
+
+ std::set<mach_msg_id_t> ids =
+ universal_mach_exc_server.MachMessageServerRequestIDs();
+ EXPECT_NE(ids.end(), ids.find(2405)); // There is no constant for this.
+
+ MachExceptionRaiseRequest request;
+ EXPECT_LE(request.Head.msgh_size,
+ universal_mach_exc_server.MachMessageServerRequestSize());
+
+ MachExceptionRaiseReply reply;
+ EXPECT_LE(sizeof(reply),
+ universal_mach_exc_server.MachMessageServerReplySize());
+
+ const exception_behavior_t kExceptionBehavior =
+ EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES;
+
+ EXPECT_CALL(
+ server,
+ MockCatchMachException(kExceptionBehavior,
+ kServerLocalPort,
+ kExceptionThreadPort,
+ kExceptionTaskPort,
+ kExceptionType,
+ AreExceptionCodes(kTestMachExceptionCodes[0],
+ kTestMachExceptionCodes[1]),
+ Pointee(Eq(THREAD_STATE_NONE)),
+ IsThreadStateAndCount(0u),
+ IsThreadStateAndCount(0u),
+ Eq(&request.trailer)))
+ .WillOnce(Return(KERN_SUCCESS))
+ .RetiresOnSaturation();
+
+ bool destroy_complex_request = false;
+ EXPECT_TRUE(universal_mach_exc_server.MachMessageServerFunction(
+ reinterpret_cast<mach_msg_header_t*>(&request),
+ reinterpret_cast<mach_msg_header_t*>(&reply),
+ &destroy_complex_request));
+ EXPECT_TRUE(destroy_complex_request);
+
+ reply.Verify(kExceptionBehavior);
+}
+
+TEST(ExcServerVariants, MockMachExceptionRaiseState) {
+ ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE);
+
+ MockUniversalMachExcServer server;
+ UniversalMachExcServer universal_mach_exc_server(&server);
+
+ std::set<mach_msg_id_t> ids =
+ universal_mach_exc_server.MachMessageServerRequestIDs();
+ EXPECT_NE(ids.end(), ids.find(2406)); // There is no constant for this.
+
+ MachExceptionRaiseStateRequest request;
+ EXPECT_LE(request.Head.msgh_size,
+ universal_mach_exc_server.MachMessageServerRequestSize());
+
+ MachExceptionRaiseStateReply reply;
+ EXPECT_LE(sizeof(reply),
+ universal_mach_exc_server.MachMessageServerReplySize());
+
+ const exception_behavior_t kExceptionBehavior =
+ EXCEPTION_STATE | MACH_EXCEPTION_CODES;
+
+ EXPECT_CALL(
+ server,
+ MockCatchMachException(kExceptionBehavior,
+ kServerLocalPort,
+ THREAD_NULL,
+ TASK_NULL,
+ kExceptionType,
+ AreExceptionCodes(kTestMachExceptionCodes[0],
+ kTestMachExceptionCodes[1]),
+ Pointee(Eq(kThreadStateFlavor)),
+ IsThreadStateAndCount(kThreadStateFlavorCount),
+ IsThreadStateAndCount(arraysize(reply.new_state)),
+ Eq(request.Trailer())))
+ .WillOnce(Return(KERN_SUCCESS))
+ .RetiresOnSaturation();
+
+ bool destroy_complex_request = false;
+ EXPECT_TRUE(universal_mach_exc_server.MachMessageServerFunction(
+ reinterpret_cast<mach_msg_header_t*>(&request),
+ reinterpret_cast<mach_msg_header_t*>(&reply),
+ &destroy_complex_request));
+
+ // The request wasn’t complex, so nothing got a chance to change the value of
+ // this variable.
+ EXPECT_FALSE(destroy_complex_request);
+
+ reply.Verify(kExceptionBehavior);
+}
+
+TEST(ExcServerVariants, MockMachExceptionRaiseStateIdentity) {
+ ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE);
+
+ MockUniversalMachExcServer server;
+ UniversalMachExcServer universal_mach_exc_server(&server);
+
+ std::set<mach_msg_id_t> ids =
+ universal_mach_exc_server.MachMessageServerRequestIDs();
+ EXPECT_NE(ids.end(), ids.find(2407)); // There is no constant for this.
+
+ MachExceptionRaiseStateIdentityRequest request;
+ EXPECT_LE(request.Head.msgh_size,
+ universal_mach_exc_server.MachMessageServerRequestSize());
+
+ MachExceptionRaiseStateIdentityReply reply;
+ EXPECT_LE(sizeof(reply),
+ universal_mach_exc_server.MachMessageServerReplySize());
+
+ const exception_behavior_t kExceptionBehavior =
+ EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES;
+
+ EXPECT_CALL(
+ server,
+ MockCatchMachException(kExceptionBehavior,
+ kServerLocalPort,
+ kExceptionThreadPort,
+ kExceptionTaskPort,
+ kExceptionType,
+ AreExceptionCodes(kTestMachExceptionCodes[0],
+ kTestMachExceptionCodes[1]),
+ Pointee(Eq(kThreadStateFlavor)),
+ IsThreadStateAndCount(kThreadStateFlavorCount),
+ IsThreadStateAndCount(arraysize(reply.new_state)),
+ Eq(request.Trailer())))
+ .WillOnce(Return(KERN_SUCCESS))
+ .RetiresOnSaturation();
+
+ bool destroy_complex_request = false;
+ EXPECT_TRUE(universal_mach_exc_server.MachMessageServerFunction(
+ reinterpret_cast<mach_msg_header_t*>(&request),
+ reinterpret_cast<mach_msg_header_t*>(&reply),
+ &destroy_complex_request));
+ EXPECT_TRUE(destroy_complex_request);
+
+ reply.Verify(kExceptionBehavior);
+}
+
+TEST(ExcServerVariants, MockUnknownID) {
+ ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE);
+
+ MockUniversalMachExcServer server;
+ UniversalMachExcServer universal_mach_exc_server(&server);
+
+ // Make sure that a message with an unknown ID is handled appropriately.
+ // UniversalMachExcServer should not dispatch the message to
+ // MachMessageServerFunction, but should generate a MIG_BAD_ID error reply.
+
+ const mach_msg_id_t unknown_ids[] = {
+ // Reasonable things to check.
+ -101,
+ -100,
+ -99,
+ -1,
+ 0,
+ 1,
+ 99,
+ 100,
+ 101,
+
+ // Invalid IDs right around valid ones.
+ 2400,
+ 2404,
+ 2408,
+
+ // Valid and invalid IDs in the range used for replies, not requests.
+ 2500,
+ 2501,
+ 2502,
+ 2503,
+ 2504,
+ 2505,
+ 2506,
+ 2507,
+ 2508,
+ };
+
+ for (size_t index = 0; index < arraysize(unknown_ids); ++index) {
+ mach_msg_id_t id = unknown_ids[index];
+
+ SCOPED_TRACE(base::StringPrintf("unknown id %d", id));
+
+ std::set<mach_msg_id_t> ids =
+ universal_mach_exc_server.MachMessageServerRequestIDs();
+ EXPECT_EQ(ids.end(), ids.find(id));
+
+ InvalidRequest request(id);
+ EXPECT_LE(sizeof(request),
+ universal_mach_exc_server.MachMessageServerRequestSize());
+
+ BadIDErrorReply reply;
+ EXPECT_LE(sizeof(reply),
+ universal_mach_exc_server.MachMessageServerReplySize());
+
+ bool destroy_complex_request = false;
+ EXPECT_FALSE(universal_mach_exc_server.MachMessageServerFunction(
+ reinterpret_cast<mach_msg_header_t*>(&request),
+ reinterpret_cast<mach_msg_header_t*>(&reply),
+ &destroy_complex_request));
+
+ // The request wasn’t handled, nothing got a chance to change the value of
+ // this variable. MachMessageServer would destroy the request if it was
+ // complex, regardless of what was done to this variable, because the
+ // return code was not KERN_SUCCESS or MIG_NO_REPLY.
+ EXPECT_FALSE(destroy_complex_request);
+
+ reply.Verify(id);
+ }
+}
+
+TEST(ExcServerVariants, MachMessageServerRequestIDs) {
+ std::set<mach_msg_id_t> expect_request_ids;
+
+ // There are no constants for these.
+ expect_request_ids.insert(2401);
+ expect_request_ids.insert(2402);
+ expect_request_ids.insert(2403);
+ expect_request_ids.insert(2405);
+ expect_request_ids.insert(2406);
+ expect_request_ids.insert(2407);
+
+ MockUniversalMachExcServer server;
+ UniversalMachExcServer universal_mach_exc_server(&server);
+
+ EXPECT_EQ(expect_request_ids,
+ universal_mach_exc_server.MachMessageServerRequestIDs());
+}
+
+class TestExcServerVariants : public MachMultiprocess,
+ public UniversalMachExcServer::Interface {
+ public:
+ TestExcServerVariants(exception_behavior_t behavior,
+ thread_state_flavor_t flavor,
+ mach_msg_type_number_t state_count)
+ : MachMultiprocess(),
+ UniversalMachExcServer::Interface(),
+ behavior_(behavior),
+ flavor_(flavor),
+ state_count_(state_count),
+ handled_(false) {}
+
+ // UniversalMachExcServer::Interface:
+
+ virtual kern_return_t CatchMachException(
+ exception_behavior_t behavior,
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_complex_request) override {
+ *destroy_complex_request = true;
+
+ EXPECT_FALSE(handled_);
+ handled_ = true;
+
+ EXPECT_EQ(behavior_, behavior);
+
+ EXPECT_EQ(LocalPort(), exception_port);
+
+ if (ExceptionBehaviorHasIdentity(behavior)) {
+ EXPECT_NE(THREAD_NULL, thread);
+ EXPECT_EQ(ChildTask(), task);
+ } else {
+ EXPECT_EQ(THREAD_NULL, thread);
+ EXPECT_EQ(TASK_NULL, task);
+ }
+
+ EXPECT_EQ(EXC_CRASH, exception);
+ EXPECT_EQ(2u, code_count);
+
+ // The exception and code_count checks above would ideally use ASSERT_EQ so
+ // that the next conditional would not be necessary, but ASSERT_* requires a
+ // function returning type void, and the interface dictates otherwise here.
+ if (exception == EXC_CRASH && code_count >= 1) {
+ int signal;
+ ExcCrashRecoverOriginalException(code[0], nullptr, &signal);
+ SetExpectedChildTermination(kTerminationSignal, signal);
+ }
+
+ const bool has_state = ExceptionBehaviorHasState(behavior);
+ if (has_state) {
+ EXPECT_EQ(flavor_, *flavor);
+ EXPECT_EQ(state_count_, old_state_count);
+ EXPECT_NE(nullptr, old_state);
+ EXPECT_EQ(implicit_cast<mach_msg_type_number_t>(THREAD_STATE_MAX),
+ *new_state_count);
+ EXPECT_NE(nullptr, new_state);
+ } else {
+ EXPECT_EQ(THREAD_STATE_NONE, *flavor);
+ EXPECT_EQ(0u, old_state_count);
+ EXPECT_EQ(nullptr, old_state);
+ EXPECT_EQ(0u, *new_state_count);
+ EXPECT_EQ(nullptr, new_state);
+ }
+
+ EXPECT_EQ(implicit_cast<mach_msg_trailer_type_t>(MACH_MSG_TRAILER_FORMAT_0),
+ trailer->msgh_trailer_type);
+ EXPECT_EQ(REQUESTED_TRAILER_SIZE(kMachMessageOptions),
+ trailer->msgh_trailer_size);
+
+ ExcServerCopyState(
+ behavior, old_state, old_state_count, new_state, new_state_count);
+
+ return ExcServerSuccessfulReturnValue(behavior, false);
+ }
+
+ private:
+ // MachMultiprocess:
+
+ void MachMultiprocessParent() override {
+ UniversalMachExcServer universal_mach_exc_server(this);
+
+ kern_return_t kr =
+ MachMessageServer::Run(&universal_mach_exc_server,
+ LocalPort(),
+ kMachMessageOptions,
+ MachMessageServer::kOneShot,
+ MachMessageServer::kReceiveLargeError,
+ kMachMessageTimeoutWaitIndefinitely);
+ EXPECT_EQ(KERN_SUCCESS, kr)
+ << MachErrorMessage(kr, "MachMessageServer::Run");
+
+ EXPECT_TRUE(handled_);
+ }
+
+ void MachMultiprocessChild() override {
+ // Set the parent as the exception handler for EXC_CRASH.
+ kern_return_t kr = task_set_exception_ports(
+ mach_task_self(), EXC_MASK_CRASH, RemotePort(), behavior_, flavor_);
+ ASSERT_EQ(KERN_SUCCESS, kr)
+ << MachErrorMessage(kr, "task_set_exception_ports");
+
+ // Now crash.
+ __builtin_trap();
+ }
+
+ exception_behavior_t behavior_;
+ thread_state_flavor_t flavor_;
+ mach_msg_type_number_t state_count_;
+ bool handled_;
+
+ static const mach_msg_option_t kMachMessageOptions =
+ MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0);
+
+ DISALLOW_COPY_AND_ASSIGN(TestExcServerVariants);
+};
+
+TEST(ExcServerVariants, ExceptionRaise) {
+ TestExcServerVariants test_exc_server_variants(
+ EXCEPTION_DEFAULT, THREAD_STATE_NONE, 0);
+ test_exc_server_variants.Run();
+}
+
+TEST(ExcServerVariants, ExceptionRaiseState) {
+ TestExcServerVariants test_exc_server_variants(
+ EXCEPTION_STATE, MACHINE_THREAD_STATE, MACHINE_THREAD_STATE_COUNT);
+ test_exc_server_variants.Run();
+}
+
+TEST(ExcServerVariants, ExceptionRaiseStateIdentity) {
+ TestExcServerVariants test_exc_server_variants(EXCEPTION_STATE_IDENTITY,
+ MACHINE_THREAD_STATE,
+ MACHINE_THREAD_STATE_COUNT);
+ test_exc_server_variants.Run();
+}
+
+TEST(ExcServerVariants, MachExceptionRaise) {
+ TestExcServerVariants test_exc_server_variants(
+ MACH_EXCEPTION_CODES | EXCEPTION_DEFAULT, THREAD_STATE_NONE, 0);
+ test_exc_server_variants.Run();
+}
+
+TEST(ExcServerVariants, MachExceptionRaiseState) {
+ TestExcServerVariants test_exc_server_variants(
+ MACH_EXCEPTION_CODES | EXCEPTION_STATE,
+ MACHINE_THREAD_STATE,
+ MACHINE_THREAD_STATE_COUNT);
+ test_exc_server_variants.Run();
+}
+
+TEST(ExcServerVariants, MachExceptionRaiseStateIdentity) {
+ TestExcServerVariants test_exc_server_variants(
+ MACH_EXCEPTION_CODES | EXCEPTION_STATE_IDENTITY,
+ MACHINE_THREAD_STATE,
+ MACHINE_THREAD_STATE_COUNT);
+ test_exc_server_variants.Run();
+}
+
+TEST(ExcServerVariants, ThreadStates) {
+ // So far, all of the tests worked with MACHINE_THREAD_STATE. Now try all of
+ // the other thread state flavors that are expected to work.
+
+ struct TestData {
+ thread_state_flavor_t flavor;
+ mach_msg_type_number_t count;
+ };
+ const TestData test_data[] = {
+#if defined(ARCH_CPU_X86_FAMILY)
+ // For the x86 family, exception handlers can only properly receive the
+ // thread, float, and exception state flavors. There’s a bug in the kernel
+ // that causes it to call thread_getstatus() (a wrapper for the more
+ // familiar thread_get_state()) with an incorrect state buffer size
+ // parameter when delivering an exception. 10.9.4
+ // xnu-2422.110.17/osfmk/kern/exception.c exception_deliver() uses the
+ // _MachineStateCount[] array indexed by the flavor number to obtain the
+ // buffer size. 10.9.4 xnu-2422.110.17/osfmk/i386/pcb.c contains the
+ // definition of this array for the x86 family. The slots corresponding to
+ // thread, float, and exception state flavors in both native-width (32-
+ // and 64-bit) and universal are correct, but the remaining elements in
+ // the array are not. This includes elements that would correspond to
+ // debug and AVX state flavors, so these cannot be tested here.
+ //
+ // When machine_thread_get_state() (the machine-specific implementation of
+ // thread_get_state()) encounters an undersized buffer as reported by the
+ // buffer size parameter, it returns KERN_INVALID_ARGUMENT, which causes
+ // exception_deliver() to not actually deliver the exception and instead
+ // return that error code to exception_triage() as well.
+ //
+ // This bug is filed as radar 18312067.
+ //
+ // Additionaly, the AVX state flavors are also not tested because they’re
+ // not available on all CPUs and OS versions.
+#if defined(ARCH_CPU_X86)
+ {x86_THREAD_STATE32, x86_THREAD_STATE32_COUNT},
+ {x86_FLOAT_STATE32, x86_FLOAT_STATE32_COUNT},
+ {x86_EXCEPTION_STATE32, x86_EXCEPTION_STATE32_COUNT},
+#endif
+#if defined(ARCH_CPU_X86_64)
+ {x86_THREAD_STATE64, x86_THREAD_STATE64_COUNT},
+ {x86_FLOAT_STATE64, x86_FLOAT_STATE64_COUNT},
+ {x86_EXCEPTION_STATE64, x86_EXCEPTION_STATE64_COUNT},
+#endif
+ {x86_THREAD_STATE, x86_THREAD_STATE_COUNT},
+ {x86_FLOAT_STATE, x86_FLOAT_STATE_COUNT},
+ {x86_EXCEPTION_STATE, x86_EXCEPTION_STATE_COUNT},
+#else
+#error Port this test to your CPU architecture.
+#endif
+ };
+
+ for (size_t index = 0; index < arraysize(test_data); ++index) {
+ const TestData& test = test_data[index];
+ SCOPED_TRACE(
+ base::StringPrintf("index %zu, flavor %d", index, test.flavor));
+
+ TestExcServerVariants test_exc_server_variants(
+ MACH_EXCEPTION_CODES | EXCEPTION_STATE_IDENTITY,
+ test.flavor,
+ test.count);
+ test_exc_server_variants.Run();
+ }
+}
+
+TEST(ExcServerVariants, ExcServerSuccessfulReturnValue) {
+ struct TestData {
+ exception_behavior_t behavior;
+ bool set_thread_state;
+ kern_return_t kr;
+ };
+ const TestData kTestData[] = {
+ {EXCEPTION_DEFAULT, false, KERN_SUCCESS},
+ {EXCEPTION_STATE, false, MACH_RCV_PORT_DIED},
+ {EXCEPTION_STATE_IDENTITY, false, MACH_RCV_PORT_DIED},
+ {kMachExceptionCodes | EXCEPTION_DEFAULT, false, KERN_SUCCESS},
+ {kMachExceptionCodes | EXCEPTION_STATE, false, MACH_RCV_PORT_DIED},
+ {kMachExceptionCodes | EXCEPTION_STATE_IDENTITY,
+ false,
+ MACH_RCV_PORT_DIED},
+ {EXCEPTION_DEFAULT, true, KERN_SUCCESS},
+ {EXCEPTION_STATE, true, KERN_SUCCESS},
+ {EXCEPTION_STATE_IDENTITY, true, KERN_SUCCESS},
+ {kMachExceptionCodes | EXCEPTION_DEFAULT, true, KERN_SUCCESS},
+ {kMachExceptionCodes | EXCEPTION_STATE, true, KERN_SUCCESS},
+ {kMachExceptionCodes | EXCEPTION_STATE_IDENTITY, true, KERN_SUCCESS},
+ };
+
+ for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ const TestData& test_data = kTestData[index];
+ SCOPED_TRACE(
+ base::StringPrintf("index %zu, behavior %d, set_thread_state %s",
+ index,
+ test_data.behavior,
+ test_data.set_thread_state ? "true" : "false"));
+
+ EXPECT_EQ(test_data.kr,
+ ExcServerSuccessfulReturnValue(test_data.behavior,
+ test_data.set_thread_state));
+ }
+}
+
+TEST(ExcServerVariants, ExcServerCopyState) {
+ const natural_t old_state[] = {1, 2, 3, 4, 5};
+ natural_t new_state[10] = {};
+
+ const mach_msg_type_number_t old_state_count = arraysize(old_state);
+ mach_msg_type_number_t new_state_count = arraysize(new_state);
+
+ // EXCEPTION_DEFAULT (with or without MACH_EXCEPTION_CODES) is not
+ // state-carrying. new_state and new_state_count should be untouched.
+ ExcServerCopyState(EXCEPTION_DEFAULT,
+ old_state,
+ old_state_count,
+ new_state,
+ &new_state_count);
+ EXPECT_EQ(arraysize(new_state), new_state_count);
+ for (size_t i = 0; i < arraysize(new_state); ++i) {
+ EXPECT_EQ(0u, new_state[i]) << "i " << i;
+ }
+
+ ExcServerCopyState(MACH_EXCEPTION_CODES | EXCEPTION_DEFAULT,
+ old_state,
+ old_state_count,
+ new_state,
+ &new_state_count);
+ EXPECT_EQ(arraysize(new_state), new_state_count);
+ for (size_t i = 0; i < arraysize(new_state); ++i) {
+ EXPECT_EQ(0u, new_state[i]) << "i " << i;
+ }
+
+ // This is a state-carrying exception where old_state_count is small.
+ mach_msg_type_number_t copy_limit = 2;
+ ExcServerCopyState(
+ EXCEPTION_STATE, old_state, copy_limit, new_state, &new_state_count);
+ EXPECT_EQ(copy_limit, new_state_count);
+ for (size_t i = 0; i < copy_limit; ++i) {
+ EXPECT_EQ(old_state[i], new_state[i]) << "i " << i;
+ }
+ for (size_t i = copy_limit; i < arraysize(new_state); ++i) {
+ EXPECT_EQ(0u, new_state[i]) << "i " << i;
+ }
+
+ // This is a state-carrying exception where new_state_count is small.
+ copy_limit = 3;
+ new_state_count = copy_limit;
+ ExcServerCopyState(EXCEPTION_STATE_IDENTITY,
+ old_state,
+ old_state_count,
+ new_state,
+ &new_state_count);
+ EXPECT_EQ(copy_limit, new_state_count);
+ for (size_t i = 0; i < copy_limit; ++i) {
+ EXPECT_EQ(old_state[i], new_state[i]) << "i " << i;
+ }
+ for (size_t i = copy_limit; i < arraysize(new_state); ++i) {
+ EXPECT_EQ(0u, new_state[i]) << "i " << i;
+ }
+
+ // This is a state-carrying exception where all of old_state is copied to
+ // new_state, which is large enough to receive it and then some.
+ new_state_count = arraysize(new_state);
+ ExcServerCopyState(MACH_EXCEPTION_CODES | EXCEPTION_STATE_IDENTITY,
+ old_state,
+ old_state_count,
+ new_state,
+ &new_state_count);
+ EXPECT_EQ(old_state_count, new_state_count);
+ for (size_t i = 0; i < arraysize(old_state); ++i) {
+ EXPECT_EQ(old_state[i], new_state[i]) << "i " << i;
+ }
+ for (size_t i = arraysize(old_state); i < arraysize(new_state); ++i) {
+ EXPECT_EQ(0u, new_state[i]) << "i " << i;
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/exception_behaviors.cc b/chromium/third_party/crashpad/crashpad/util/mach/exception_behaviors.cc
new file mode 100644
index 00000000000..eb2a80caff9
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/exception_behaviors.cc
@@ -0,0 +1,39 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/exception_behaviors.h"
+
+namespace crashpad {
+
+bool ExceptionBehaviorHasState(exception_behavior_t behavior) {
+ const exception_behavior_t basic_behavior = ExceptionBehaviorBasic(behavior);
+ return basic_behavior == EXCEPTION_STATE ||
+ basic_behavior == EXCEPTION_STATE_IDENTITY;
+}
+
+bool ExceptionBehaviorHasIdentity(exception_behavior_t behavior) {
+ const exception_behavior_t basic_behavior = ExceptionBehaviorBasic(behavior);
+ return basic_behavior == EXCEPTION_DEFAULT ||
+ basic_behavior == EXCEPTION_STATE_IDENTITY;
+}
+
+bool ExceptionBehaviorHasMachExceptionCodes(exception_behavior_t behavior) {
+ return (behavior & MACH_EXCEPTION_CODES) != 0;
+}
+
+exception_behavior_t ExceptionBehaviorBasic(exception_behavior_t behavior) {
+ return behavior & ~MACH_EXCEPTION_CODES;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/exception_behaviors.h b/chromium/third_party/crashpad/crashpad/util/mach/exception_behaviors.h
new file mode 100644
index 00000000000..14b5448a2b0
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/exception_behaviors.h
@@ -0,0 +1,94 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MACH_EXCEPTION_BEHAVIORS_H_
+#define CRASHPAD_UTIL_MACH_EXCEPTION_BEHAVIORS_H_
+
+#include <mach/mach.h>
+
+namespace crashpad {
+
+//! \brief Determines whether \a behavior indicates an exception behavior that
+//! carries thread state information.
+//!
+//! When this function returns `true`, an exception message of \a behavior will
+//! carry thread state information. Its \a flavor, \a old_state, \a
+//! old_state_count, \a new_state, and \a new_state_count fields will be valid.
+//! When this function returns `false`, these fields will not be valid.
+//!
+//! Exception behaviors that carry thread state information are
+//! `EXCEPTION_STATE` and `EXCEPTION_STATE_IDENTITY`. `MACH_EXCEPTION_CODES` may
+//! also be set. These behaviors correspond to `exception_raise_state()`,
+//! `exception_raise_state_identity()`, `mach_exception_raise_state()`, and
+//! `mach_exception_raise_state_identity()`.
+//!
+//! \param[in] behavior An exception behavior value.
+//!
+//! \return `true` if \a behavior is `EXCEPTION_STATE` or
+//! `EXCEPTION_STATE_IDENTITY`, possibly with `MACH_EXCEPTION_CODES` also
+//! set.
+bool ExceptionBehaviorHasState(exception_behavior_t behavior);
+
+//! \brief Determines whether \a behavior indicates an exception behavior that
+//! carries thread and task identities.
+//!
+//! When this function returns `true`, an exception message of \a behavior will
+//! carry thread and task identities in the form of send rights to the thread
+//! and task ports. Its \a thread and \a task fields will be valid. When this
+//! function returns `false`, these fields will not be valid.
+//!
+//! Exception behaviors that carry thread and task identity information are
+//! `EXCEPTION_DEFAULT` and `EXCEPTION_STATE_IDENTITY`. `MACH_EXCEPTION_CODES`
+//! may also be set. These behaviors correspond to `exception_raise()`,
+//! `exception_raise_state_identity()`, `mach_exception_raise()`, and
+//! `mach_exception_raise_state_identity()`.
+//!
+//! \param[in] behavior An exception behavior value.
+//!
+//! \return `true` if \a behavior is `EXCEPTION_DEFAULT` or
+//! `EXCEPTION_STATE_IDENTITY`, possibly with `MACH_EXCEPTION_CODES` also
+//! set.
+bool ExceptionBehaviorHasIdentity(exception_behavior_t behavior);
+
+//! \brief Determines whether \a behavior indicates an exception behavior that
+//! carries 64-bit exception codes (“Mach exception codes”).
+//!
+//! When this function returns `true`, an exception message of \a behavior will
+//! carry 64-bit exception codes of type `mach_exception_code_t` in its \a code
+//! field. When this function returns `false`, the exception message will carry
+//! 32-bit exception codes of type `exception_data_type_t` in its \a code field.
+//!
+//! Exception behaviors that carry 64-bit exception codes are those that have
+//! `MACH_EXCEPTION_CODES` set. These behaviors correspond to
+//! `mach_exception_raise()`, `mach_exception_raise_state()`, and
+//! `mach_exception_raise_state_identity()`.
+//!
+//! \param[in] behavior An exception behavior value.
+//!
+//! \return `true` if `MACH_EXCEPTION_CODES` is set in \a behavior.
+bool ExceptionBehaviorHasMachExceptionCodes(exception_behavior_t behavior);
+
+//! \brief Returns the basic behavior value of \a behavior, its value without
+//! `MACH_EXCEPTION_CODES` set.
+//!
+//! \param[in] behavior An exception behavior value.
+//!
+//! \return `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`, or
+//! `EXCEPTION_STATE_IDENTITY`, assuming \a behavior was a correct exception
+//! behavior value.
+exception_behavior_t ExceptionBehaviorBasic(exception_behavior_t behavior);
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MACH_EXCEPTION_BEHAVIORS_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/exception_behaviors_test.cc b/chromium/third_party/crashpad/crashpad/util/mach/exception_behaviors_test.cc
new file mode 100644
index 00000000000..cbde600a4b8
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/exception_behaviors_test.cc
@@ -0,0 +1,72 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/exception_behaviors.h"
+
+#include "base/basictypes.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+#include "util/mach/mach_extensions.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(ExceptionBehaviors, ExceptionBehaviors) {
+ struct TestData {
+ exception_behavior_t behavior;
+ bool state;
+ bool identity;
+ bool mach_exception_codes;
+ exception_behavior_t basic_behavior;
+ };
+ const TestData kTestData[] = {
+ {EXCEPTION_DEFAULT, false, true, false, EXCEPTION_DEFAULT},
+ {EXCEPTION_STATE, true, false, false, EXCEPTION_STATE},
+ {EXCEPTION_STATE_IDENTITY, true, true, false, EXCEPTION_STATE_IDENTITY},
+ {kMachExceptionCodes | EXCEPTION_DEFAULT,
+ false,
+ true,
+ true,
+ EXCEPTION_DEFAULT},
+ {kMachExceptionCodes | EXCEPTION_STATE,
+ true,
+ false,
+ true,
+ EXCEPTION_STATE},
+ {kMachExceptionCodes | EXCEPTION_STATE_IDENTITY,
+ true,
+ true,
+ true,
+ EXCEPTION_STATE_IDENTITY},
+ };
+
+ for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ const TestData& test_data = kTestData[index];
+ SCOPED_TRACE(base::StringPrintf(
+ "index %zu, behavior %d", index, test_data.behavior));
+
+ EXPECT_EQ(test_data.state, ExceptionBehaviorHasState(test_data.behavior));
+ EXPECT_EQ(test_data.identity,
+ ExceptionBehaviorHasIdentity(test_data.behavior));
+ EXPECT_EQ(test_data.mach_exception_codes,
+ ExceptionBehaviorHasMachExceptionCodes(test_data.behavior));
+ EXPECT_EQ(test_data.basic_behavior,
+ ExceptionBehaviorBasic(test_data.behavior));
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/exception_ports.cc b/chromium/third_party/crashpad/crashpad/util/mach/exception_ports.cc
new file mode 100644
index 00000000000..2d85c1e67a4
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/exception_ports.cc
@@ -0,0 +1,133 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/exception_ports.h"
+
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+
+namespace crashpad {
+
+ExceptionPorts::ExceptionPorts(TargetType target_type, mach_port_t target_port)
+ : target_port_(target_port), dealloc_target_port_(false) {
+ switch (target_type) {
+ case kTargetTypeHost:
+ get_exception_ports_ = host_get_exception_ports;
+ set_exception_ports_ = host_set_exception_ports;
+ target_name_ = "host";
+ if (target_port_ == HOST_NULL) {
+ target_port_ = mach_host_self();
+ dealloc_target_port_ = true;
+ }
+ break;
+
+ case kTargetTypeTask:
+ get_exception_ports_ = task_get_exception_ports;
+ set_exception_ports_ = task_set_exception_ports;
+ target_name_ = "task";
+ if (target_port_ == TASK_NULL) {
+ target_port_ = mach_task_self();
+ // Don’t deallocate mach_task_self().
+ }
+ break;
+
+ case kTargetTypeThread:
+ get_exception_ports_ = thread_get_exception_ports;
+ set_exception_ports_ = thread_set_exception_ports;
+ target_name_ = "thread";
+ if (target_port_ == THREAD_NULL) {
+ target_port_ = mach_thread_self();
+ dealloc_target_port_ = true;
+ }
+ break;
+
+ default:
+ NOTREACHED();
+ get_exception_ports_ = nullptr;
+ set_exception_ports_ = nullptr;
+ target_name_ = nullptr;
+ target_port_ = MACH_PORT_NULL;
+ break;
+ }
+}
+
+ExceptionPorts::~ExceptionPorts() {
+ if (dealloc_target_port_) {
+ kern_return_t kr = mach_port_deallocate(mach_task_self(), target_port_);
+ MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_port_deallocate";
+ }
+}
+
+bool ExceptionPorts::GetExceptionPorts(
+ exception_mask_t mask,
+ std::vector<ExceptionHandler>* handlers) const {
+ // <mach/mach_types.defs> says that these arrays have room for 32 elements,
+ // despite EXC_TYPES_COUNT only being as low as 11 (in the 10.6 SDK), and
+ // later operating system versions have defined more exception types. The
+ // generated task_get_exception_ports() in taskUser.c expects there to be room
+ // for 32.
+ const int kMaxPorts = 32;
+
+ // task_get_exception_ports() doesn’t actually use the initial value of
+ // handler_count, but 10.9.4
+ // xnu-2422.110.17/osfmk/man/task_get_exception_ports.html says it does. Humor
+ // the documentation.
+ mach_msg_type_number_t handler_count = kMaxPorts;
+
+ exception_mask_t masks[kMaxPorts];
+ exception_handler_t ports[kMaxPorts];
+ exception_behavior_t behaviors[kMaxPorts];
+ thread_state_flavor_t flavors[kMaxPorts];
+
+ kern_return_t kr = get_exception_ports_(
+ target_port_, mask, masks, &handler_count, ports, behaviors, flavors);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << TargetTypeName() << "_get_exception_ports";
+ return false;
+ }
+
+ handlers->clear();
+ for (mach_msg_type_number_t index = 0; index < handler_count; ++index) {
+ if (ports[index] != MACH_PORT_NULL) {
+ ExceptionHandler handler;
+ handler.mask = masks[index];
+ handler.port = ports[index];
+ handler.behavior = behaviors[index];
+ handler.flavor = flavors[index];
+ handlers->push_back(handler);
+ }
+ }
+
+ return true;
+}
+
+bool ExceptionPorts::SetExceptionPort(exception_mask_t mask,
+ exception_handler_t port,
+ exception_behavior_t behavior,
+ thread_state_flavor_t flavor) const {
+ kern_return_t kr =
+ set_exception_ports_(target_port_, mask, port, behavior, flavor);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << TargetTypeName() << "_set_exception_ports";
+ return false;
+ }
+
+ return true;
+}
+
+const char* ExceptionPorts::TargetTypeName() const {
+ return target_name_;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/exception_ports.h b/chromium/third_party/crashpad/crashpad/util/mach/exception_ports.h
new file mode 100644
index 00000000000..d82ac2dcf5d
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/exception_ports.h
@@ -0,0 +1,187 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MACH_EXCEPTION_PORTS_H_
+#define CRASHPAD_UTIL_MACH_EXCEPTION_PORTS_H_
+
+#include <mach/mach.h>
+
+#include <vector>
+
+#include "base/basictypes.h"
+
+namespace crashpad {
+
+//! \brief A better interface to `*_get_exception_ports()` and
+//! `*_set_exception_ports()`.
+//!
+//! The same generic interface can be used to operate on host, task, and thread
+//! exception ports. The “get” interface is superior to the system’s native
+//! interface because it keeps related data about a single exception handler
+//! together in one struct, rather than separating it into four parallel arrays.
+class ExceptionPorts {
+ public:
+ //! \brief Various entities which can have their own exception ports set.
+ enum TargetType {
+ //! \brief The host exception target.
+ //!
+ //! `host_get_exception_ports()` and `host_set_exception_ports()` will be
+ //! used. If no target port is explicitly provided, `mach_host_self()` will
+ //! be used as the target port. `mach_host_self()` is the only target port
+ //! for this type that is expected to function properly.
+ //!
+ //! \note Operations on this target type are not expected to succeed as
+ //! non-root, because `mach_host_self()` doesn’t return the privileged
+ //! `host_priv` port to non-root users, and this is the target port
+ //! that’s required for `host_get_exception_ports()` and
+ //! `host_set_exception_ports()`.
+ kTargetTypeHost = 0,
+
+ //! \brief A task exception target.
+ //!
+ //! `task_get_exception_ports()` and `task_set_exception_ports()` will be
+ //! used. If no target port is explicitly provided, `mach_task_self()` will
+ //! be used as the target port.
+ kTargetTypeTask,
+
+ //! \brief A thread exception target.
+ //!
+ //! `thread_get_exception_ports()` and `thread_set_exception_ports()` will
+ //! be used. If no target port is explicitly provided, `mach_thread_self()`
+ //! will be used as the target port.
+ kTargetTypeThread,
+ };
+
+ //! \brief Information about a registered exception handler.
+ struct ExceptionHandler {
+ //! \brief A mask specifying the exception types to direct to \a port,
+ //! containing `EXC_MASK_*` values.
+ exception_mask_t mask;
+
+ //! \brief A send right to a Mach port that will handle exceptions of the
+ //! types indicated in \a mask.
+ exception_handler_t port;
+
+ //! \brief The “behavior” that the exception handler at \a port implements:
+ //! `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`, or
+ //! `EXCEPTION_STATE_IDENTITY`, possibly combined with
+ //! `MACH_EXCEPTION_CODES`.
+ exception_behavior_t behavior;
+
+ //! \brief The thread state flavor that the exception handler at \a port
+ //! will receive and possibly modify. This member has no effect for \a
+ //! \a behavior values that indicate a “default” behavior.
+ thread_state_flavor_t flavor;
+ };
+
+ //! \brief Constructs an interface object to get or set exception ports on a
+ //! host, task, or thread port.
+ //!
+ //! \param[in] target_type The type of target on which the exception ports are
+ //! to be get or set: #kTargetTypeHost, #kTargetTypeTask, or or
+ //! #kTargetTypeThread. The correct functions for
+ //! `*_get_exception_ports()` and `*_set_exception_ports()` will be used.
+ //! \param[in] target_port The target on which to call
+ //! `*_get_exception_ports()` or `*_set_exception_ports()`. The target
+ //! port must be a send right to a port of the type specified in \a
+ //! target_type. In this case, ownership of \a target_port is not given to
+ //! the new ExceptionPorts object. \a target_port may also be
+ //! `HOST_NULL`, `TASK_NULL`, or `THREAD_NULL`, in which case
+ //! `mach_host_self()`, `mach_task_self()`, or `mach_thread_self()` will
+ //! be used as the target port depending on the value of \a target_type.
+ //! In this case, ownership of the target port will be managed
+ //! appropriately for \a target_type.
+ ExceptionPorts(TargetType target_type, mach_port_t target_port);
+
+ ~ExceptionPorts();
+
+ //! \brief Calls `*_get_exception_ports()` on the target.
+ //!
+ //! \param[in] mask The exception mask, containing the `EXC_MASK_*` values to
+ //! be looked up and returned in \a handlers.
+ //! \param[out] handlers The exception handlers registered for \a target_port
+ //! to handle exceptions indicated in \a mask. The caller must take
+ //! ownership of the \a port members of the returned ExceptionHandler
+ //! objects. If no execption port is registered for a bit in \a mask, \a
+ //! handlers will not contain an entry corresponding to that bit. This is
+ //! a departure from the `*_get_exception_ports()` functions, which may
+ //! return a handler whose port is set to `EXCEPTION_PORT_NULL` in this
+ //! case. On failure, this argument is untouched.
+ //!
+ //! \return `true` if `*_get_exception_ports()` returned `KERN_SUCCESS`, with
+ //! \a handlers set appropriately. `false` otherwise, with an appropriate
+ //! message logged.
+ bool GetExceptionPorts(exception_mask_t mask,
+ std::vector<ExceptionHandler>* handlers) const;
+
+ //! \brief Calls `*_set_exception_ports()` on the target.
+ //!
+ //! \param[in] mask A mask specifying the exception types to direct to \a
+ //! port, containing `EXC_MASK_*` values.
+ //! \param[in] port A send right to a Mach port that will handle exceptions
+ //! sustained by \a target_port of the types indicated in \a mask. The
+ //! send right is copied, not consumed, by this call.
+ //! \param[in] behavior The “behavior” that the exception handler at \a port
+ //! implements: `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`, or
+ //! `EXCEPTION_STATE_IDENTITY`, possibly combined with
+ //! `MACH_EXCEPTION_CODES`.
+ //! \param[in] flavor The thread state flavor that the exception handler at \a
+ //! port expects to receive and possibly modify. This argument has no
+ //! effect for \a behavior values that indicate a “default” behavior.
+ //!
+ //! \return `true` if `*_set_exception_ports()` returned `KERN_SUCCESS`.
+ //! `false` otherwise, with an appropriate message logged.
+ bool SetExceptionPort(exception_mask_t mask,
+ exception_handler_t port,
+ exception_behavior_t behavior,
+ thread_state_flavor_t flavor) const;
+
+ //! \brief Returns a string identifying the target type.
+ //!
+ //! \return `"host"`, `"task"`, or `"thread"`, as appropriate.
+ const char* TargetTypeName() const;
+
+ private:
+ using GetExceptionPortsType = kern_return_t(*)(mach_port_t,
+ exception_mask_t,
+ exception_mask_array_t,
+ mach_msg_type_number_t*,
+ exception_handler_array_t,
+ exception_behavior_array_t,
+ exception_flavor_array_t);
+
+ using SetExceptionPortsType = kern_return_t(*)(mach_port_t,
+ exception_mask_t,
+ exception_handler_t,
+ exception_behavior_t,
+ thread_state_flavor_t);
+
+ GetExceptionPortsType get_exception_ports_;
+ SetExceptionPortsType set_exception_ports_;
+ const char* target_name_;
+ mach_port_t target_port_;
+
+ // If true, target_port_ will be deallocated in the destructor. This will
+ // always be false when the user provides a non-null target_port to the
+ // constructor. It will also be false when target_type is kTargetTypeTask,
+ // even with a TASK_NULL target_port, because it is incorrect to deallocate
+ // the result of mach_task_self().
+ bool dealloc_target_port_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExceptionPorts);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MACH_EXCEPTION_PORTS_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/exception_ports_test.cc b/chromium/third_party/crashpad/crashpad/util/mach/exception_ports_test.cc
new file mode 100644
index 00000000000..6081c02def8
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/exception_ports_test.cc
@@ -0,0 +1,616 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/exception_ports.h"
+
+#include <mach/mach.h>
+#include <pthread.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+#include "test/mac/mach_errors.h"
+#include "test/mac/mach_multiprocess.h"
+#include "util/file/file_io.h"
+#include "util/mach/exc_server_variants.h"
+#include "util/mach/exception_types.h"
+#include "util/mach/mach_extensions.h"
+#include "util/mach/mach_message.h"
+#include "util/mach/mach_message_server.h"
+#include "util/misc/scoped_forbid_return.h"
+#include "util/synchronization/semaphore.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+// Calls GetExceptionPorts() on its |exception_ports| argument to look up the
+// EXC_MASK_CRASH handler. If |expect_port| is not MACH_PORT_NULL, it expects to
+// find a handler for this mask whose port matches |expect_port| and whose
+// behavior matches |expect_behavior| exactly. In this case, if
+// |expect_behavior| is a state-carrying behavior, the looked-up thread state
+// flavor is expected to be MACHINE_THREAD_STATE, otherwise, it is expected to
+// be THREAD_STATE_NONE. If |expect_port| is MACH_PORT_NULL, no handler for
+// EXC_MASK_CRASH is expected to be found.
+//
+// A second GetExceptionPorts() lookup is also performed on a wider exception
+// mask, EXC_MASK_ALL | EXC_MASK_CRASH. The EXC_MASK_CRASH handler’s existence
+// and properties from this second lookup are validated in the same way.
+//
+// This function uses gtest EXPECT_* and ASSERT_* macros to perform its
+// validation.
+void TestGetExceptionPorts(const ExceptionPorts& exception_ports,
+ mach_port_t expect_port,
+ exception_behavior_t expect_behavior) {
+ const exception_mask_t kExceptionMask = EXC_MASK_CRASH;
+
+ thread_state_flavor_t expect_flavor = (expect_behavior == EXCEPTION_DEFAULT)
+ ? THREAD_STATE_NONE
+ : MACHINE_THREAD_STATE;
+
+ std::vector<ExceptionPorts::ExceptionHandler> crash_handler;
+ ASSERT_TRUE(
+ exception_ports.GetExceptionPorts(kExceptionMask, &crash_handler));
+
+ if (expect_port != MACH_PORT_NULL) {
+ ASSERT_EQ(1u, crash_handler.size());
+ base::mac::ScopedMachSendRight port_owner(crash_handler[0].port);
+
+ EXPECT_EQ(kExceptionMask, crash_handler[0].mask);
+ EXPECT_EQ(expect_port, crash_handler[0].port);
+ EXPECT_EQ(expect_behavior, crash_handler[0].behavior);
+ EXPECT_EQ(expect_flavor, crash_handler[0].flavor);
+ } else {
+ EXPECT_TRUE(crash_handler.empty());
+ }
+
+ std::vector<ExceptionPorts::ExceptionHandler> handlers;
+ ASSERT_TRUE(exception_ports.GetExceptionPorts(
+ ExcMaskAll() | EXC_MASK_CRASH, &handlers));
+
+ EXPECT_GE(handlers.size(), crash_handler.size());
+ bool found = false;
+ for (const ExceptionPorts::ExceptionHandler& handler : handlers) {
+ if ((handler.mask & kExceptionMask) != 0) {
+ base::mac::ScopedMachSendRight port_owner(handler.port);
+
+ EXPECT_FALSE(found);
+ found = true;
+ EXPECT_EQ(expect_port, handler.port);
+ EXPECT_EQ(expect_behavior, handler.behavior);
+ EXPECT_EQ(expect_flavor, handler.flavor);
+ }
+ }
+
+ if (expect_port != MACH_PORT_NULL) {
+ EXPECT_TRUE(found);
+ } else {
+ EXPECT_FALSE(found);
+ }
+}
+
+class TestExceptionPorts : public MachMultiprocess,
+ public UniversalMachExcServer::Interface {
+ public:
+ // Which entities to set exception ports for.
+ enum SetOn {
+ kSetOnTaskOnly = 0,
+ kSetOnTaskAndThreads,
+ };
+
+ // Where to call ExceptionPorts::SetExceptionPort() from.
+ enum SetType {
+ // Call it from the child process on itself.
+ kSetInProcess = 0,
+
+ // Call it from the parent process on the child.
+ kSetOutOfProcess,
+ };
+
+ // Which thread in the child process is expected to crash.
+ enum WhoCrashes {
+ kNobodyCrashes = 0,
+ kMainThreadCrashes,
+ kOtherThreadCrashes,
+ };
+
+ TestExceptionPorts(SetOn set_on, SetType set_type, WhoCrashes who_crashes)
+ : MachMultiprocess(),
+ UniversalMachExcServer::Interface(),
+ set_on_(set_on),
+ set_type_(set_type),
+ who_crashes_(who_crashes),
+ handled_(false) {}
+
+ SetOn set_on() const { return set_on_; }
+ SetType set_type() const { return set_type_; }
+ WhoCrashes who_crashes() const { return who_crashes_; }
+
+ // UniversalMachExcServer::Interface:
+
+ virtual kern_return_t CatchMachException(
+ exception_behavior_t behavior,
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_complex_request) override {
+ *destroy_complex_request = true;
+
+ EXPECT_FALSE(handled_);
+ handled_ = true;
+
+ // To be able to distinguish between which handler was actually triggered,
+ // the different handlers are registered with different behavior values.
+ exception_behavior_t expect_behavior;
+ if (set_on_ == kSetOnTaskOnly) {
+ expect_behavior = EXCEPTION_DEFAULT;
+ } else if (who_crashes_ == kMainThreadCrashes) {
+ expect_behavior = EXCEPTION_STATE;
+ } else if (who_crashes_ == kOtherThreadCrashes) {
+ expect_behavior = EXCEPTION_STATE_IDENTITY;
+ } else {
+ NOTREACHED();
+ expect_behavior = 0;
+ }
+
+ EXPECT_EQ(expect_behavior, behavior);
+
+ EXPECT_EQ(LocalPort(), exception_port);
+
+ EXPECT_EQ(EXC_CRASH, exception);
+ EXPECT_EQ(2u, code_count);
+
+ // The exception and code_count checks above would ideally use ASSERT_EQ so
+ // that the next conditional would not be necessary, but ASSERT_* requires a
+ // function returning type void, and the interface dictates otherwise here.
+ if (exception == EXC_CRASH && code_count >= 1) {
+ int signal;
+ ExcCrashRecoverOriginalException(code[0], nullptr, &signal);
+
+ // The child crashed with __builtin_trap(), which shows up as SIGILL.
+ EXPECT_EQ(SIGILL, signal);
+
+ SetExpectedChildTermination(kTerminationSignal, signal);
+ }
+
+ EXPECT_EQ(0, AuditPIDFromMachMessageTrailer(trailer));
+
+ return ExcServerSuccessfulReturnValue(behavior, false);
+ }
+
+ private:
+ class Child {
+ public:
+ explicit Child(TestExceptionPorts* test_exception_ports)
+ : test_exception_ports_(test_exception_ports),
+ thread_(),
+ init_semaphore_(0),
+ crash_semaphore_(0) {}
+
+ ~Child() {}
+
+ void Run() {
+ ExceptionPorts self_task_ports(ExceptionPorts::kTargetTypeTask,
+ TASK_NULL);
+ ExceptionPorts self_thread_ports(ExceptionPorts::kTargetTypeThread,
+ THREAD_NULL);
+
+ mach_port_t remote_port = test_exception_ports_->RemotePort();
+
+ // Set the task’s and this thread’s exception ports, if appropriate.
+ if (test_exception_ports_->set_type() == kSetInProcess) {
+ ASSERT_TRUE(self_task_ports.SetExceptionPort(
+ EXC_MASK_CRASH, remote_port, EXCEPTION_DEFAULT, THREAD_STATE_NONE));
+
+ if (test_exception_ports_->set_on() == kSetOnTaskAndThreads) {
+ ASSERT_TRUE(self_thread_ports.SetExceptionPort(EXC_MASK_CRASH,
+ remote_port,
+ EXCEPTION_STATE,
+ MACHINE_THREAD_STATE));
+ }
+ }
+
+ int rv_int = pthread_create(&thread_, nullptr, ThreadMainThunk, this);
+ ASSERT_EQ(0, rv_int);
+
+ // Wait for the new thread to be ready.
+ init_semaphore_.Wait();
+
+ // Tell the parent process that everything is set up.
+ char c = '\0';
+ CheckedWriteFile(test_exception_ports_->WritePipeHandle(), &c, 1);
+
+ // Wait for the parent process to say that its end is set up.
+ CheckedReadFile(test_exception_ports_->ReadPipeHandle(), &c, 1);
+ EXPECT_EQ('\0', c);
+
+ // Regardless of where ExceptionPorts::SetExceptionPort() ran,
+ // ExceptionPorts::GetExceptionPorts() can always be tested in-process.
+ {
+ SCOPED_TRACE("task");
+ TestGetExceptionPorts(self_task_ports, remote_port, EXCEPTION_DEFAULT);
+ }
+
+ {
+ SCOPED_TRACE("main_thread");
+ mach_port_t thread_handler =
+ (test_exception_ports_->set_on() == kSetOnTaskAndThreads)
+ ? remote_port
+ : MACH_PORT_NULL;
+ TestGetExceptionPorts(
+ self_thread_ports, thread_handler, EXCEPTION_STATE);
+ }
+
+ // Let the other thread know it’s safe to proceed.
+ crash_semaphore_.Signal();
+
+ // If this thread is the one that crashes, do it.
+ if (test_exception_ports_->who_crashes() == kMainThreadCrashes) {
+ Crash();
+ }
+
+ // Reap the other thread.
+ rv_int = pthread_join(thread_, nullptr);
+ ASSERT_EQ(0, rv_int);
+ }
+
+ private:
+ // Calls ThreadMain().
+ static void* ThreadMainThunk(void* argument) {
+ Child* self = reinterpret_cast<Child*>(argument);
+ return self->ThreadMain();
+ }
+
+ // Runs the “other” thread.
+ void* ThreadMain() {
+ ExceptionPorts self_thread_ports(ExceptionPorts::kTargetTypeThread,
+ THREAD_NULL);
+ mach_port_t remote_port = test_exception_ports_->RemotePort();
+
+ // Set this thread’s exception handler, if appropriate.
+ if (test_exception_ports_->set_type() == kSetInProcess &&
+ test_exception_ports_->set_on() == kSetOnTaskAndThreads) {
+ CHECK(self_thread_ports.SetExceptionPort(EXC_MASK_CRASH,
+ remote_port,
+ EXCEPTION_STATE_IDENTITY,
+ MACHINE_THREAD_STATE));
+ }
+
+ // Let the main thread know that this thread is ready.
+ init_semaphore_.Signal();
+
+ // Wait for the main thread to signal that it’s safe to proceed.
+ crash_semaphore_.Wait();
+
+ // Regardless of where ExceptionPorts::SetExceptionPort() ran,
+ // ExceptionPorts::GetExceptionPorts() can always be tested in-process.
+ {
+ SCOPED_TRACE("other_thread");
+ mach_port_t thread_handler =
+ (test_exception_ports_->set_on() == kSetOnTaskAndThreads)
+ ? remote_port
+ : MACH_PORT_NULL;
+ TestGetExceptionPorts(
+ self_thread_ports, thread_handler, EXCEPTION_STATE_IDENTITY);
+ }
+
+ // If this thread is the one that crashes, do it.
+ if (test_exception_ports_->who_crashes() == kOtherThreadCrashes) {
+ Crash();
+ }
+
+ return nullptr;
+ }
+
+ static void Crash() {
+ __builtin_trap();
+ }
+
+ // The parent object.
+ TestExceptionPorts* test_exception_ports_; // weak
+
+ // The “other” thread.
+ pthread_t thread_;
+
+ // The main thread waits on this for the other thread to start up and
+ // perform its own initialization.
+ Semaphore init_semaphore_;
+
+ // The child thread waits on this for the parent thread to indicate that the
+ // child can test its exception ports and possibly crash, as appropriate.
+ Semaphore crash_semaphore_;
+
+ DISALLOW_COPY_AND_ASSIGN(Child);
+ };
+
+ // MachMultiprocess:
+
+ void MachMultiprocessParent() override {
+ // Wait for the child process to be ready. It needs to have all of its
+ // threads set up before proceeding if in kSetOutOfProcess mode.
+ char c;
+ CheckedReadFile(ReadPipeHandle(), &c, 1);
+ EXPECT_EQ('\0', c);
+
+ mach_port_t local_port = LocalPort();
+
+ // Get an ExceptionPorts object for the task and each of its threads.
+ ExceptionPorts task_ports(ExceptionPorts::kTargetTypeTask, ChildTask());
+ EXPECT_EQ("task", task_ports.TargetTypeName());
+
+ // Hopefully the threads returned by task_threads() are in order, with the
+ // main thread first and the other thread second. This is currently always
+ // the case, although nothing guarantees that it will remain so.
+ thread_act_array_t threads;
+ mach_msg_type_number_t thread_count = 0;
+ kern_return_t kr = task_threads(ChildTask(), &threads, &thread_count);
+ ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "task_threads");
+
+ ScopedForbidReturn threads_need_owners;
+ ASSERT_EQ(2u, thread_count);
+ base::mac::ScopedMachSendRight main_thread(threads[0]);
+ base::mac::ScopedMachSendRight other_thread(threads[1]);
+ threads_need_owners.Disarm();
+
+ ExceptionPorts main_thread_ports(ExceptionPorts::kTargetTypeThread,
+ main_thread);
+ ExceptionPorts other_thread_ports(ExceptionPorts::kTargetTypeThread,
+ other_thread);
+ EXPECT_EQ("thread", main_thread_ports.TargetTypeName());
+ EXPECT_EQ("thread", other_thread_ports.TargetTypeName());
+
+ if (set_type_ == kSetOutOfProcess) {
+ // Test ExceptionPorts::SetExceptionPorts() being called from
+ // out-of-process.
+ //
+ // local_port is only a receive right, but a send right is needed for
+ // ExceptionPorts::SetExceptionPort(). Make a send right, which can be
+ // deallocated once the calls to ExceptionPorts::SetExceptionPort() are
+ // done.
+ kr = mach_port_insert_right(
+ mach_task_self(), local_port, local_port, MACH_MSG_TYPE_MAKE_SEND);
+ ASSERT_EQ(KERN_SUCCESS, kr)
+ << MachErrorMessage(kr, "mach_port_insert_right");
+ base::mac::ScopedMachSendRight send_owner(local_port);
+
+ ASSERT_TRUE(task_ports.SetExceptionPort(
+ EXC_MASK_CRASH, local_port, EXCEPTION_DEFAULT, THREAD_STATE_NONE));
+
+ if (set_on_ == kSetOnTaskAndThreads) {
+ ASSERT_TRUE(main_thread_ports.SetExceptionPort(
+ EXC_MASK_CRASH, local_port, EXCEPTION_STATE, MACHINE_THREAD_STATE));
+
+ ASSERT_TRUE(
+ other_thread_ports.SetExceptionPort(EXC_MASK_CRASH,
+ local_port,
+ EXCEPTION_STATE_IDENTITY,
+ MACHINE_THREAD_STATE));
+ }
+ }
+
+ // Regardless of where ExceptionPorts::SetExceptionPort() ran,
+ // ExceptionPorts::GetExceptionPorts() can always be tested out-of-process.
+ {
+ SCOPED_TRACE("task");
+ TestGetExceptionPorts(task_ports, local_port, EXCEPTION_DEFAULT);
+ }
+
+ mach_port_t thread_handler =
+ (set_on_ == kSetOnTaskAndThreads) ? local_port : MACH_PORT_NULL;
+
+ {
+ SCOPED_TRACE("main_thread");
+ TestGetExceptionPorts(main_thread_ports, thread_handler, EXCEPTION_STATE);
+ }
+
+ {
+ SCOPED_TRACE("other_thread");
+ TestGetExceptionPorts(
+ other_thread_ports, thread_handler, EXCEPTION_STATE_IDENTITY);
+ }
+
+ // Let the child process know that everything in the parent process is set
+ // up.
+ c = '\0';
+ CheckedWriteFile(WritePipeHandle(), &c, 1);
+
+ if (who_crashes_ != kNobodyCrashes) {
+ UniversalMachExcServer universal_mach_exc_server(this);
+
+ const mach_msg_timeout_t kTimeoutMs = 50;
+ kern_return_t kr =
+ MachMessageServer::Run(&universal_mach_exc_server,
+ local_port,
+ kMachMessageReceiveAuditTrailer,
+ MachMessageServer::kOneShot,
+ MachMessageServer::kReceiveLargeError,
+ kTimeoutMs);
+ EXPECT_EQ(KERN_SUCCESS, kr)
+ << MachErrorMessage(kr, "MachMessageServer::Run");
+
+ EXPECT_TRUE(handled_);
+ }
+
+ // Wait for the child process to exit or terminate, as indicated by it
+ // closing its pipe. This keeps LocalPort() alive in the child as
+ // RemotePort(), for the child’s use in its TestGetExceptionPorts().
+ CheckedReadFileAtEOF(ReadPipeHandle());
+ }
+
+ void MachMultiprocessChild() override {
+ Child child(this);
+ child.Run();
+ }
+
+ SetOn set_on_;
+ SetType set_type_;
+ WhoCrashes who_crashes_;
+
+ // true if an exception message was handled.
+ bool handled_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestExceptionPorts);
+};
+
+TEST(ExceptionPorts, TaskExceptionPorts_SetInProcess_NoCrash) {
+ TestExceptionPorts test_exception_ports(
+ TestExceptionPorts::kSetOnTaskOnly,
+ TestExceptionPorts::kSetInProcess,
+ TestExceptionPorts::kNobodyCrashes);
+ test_exception_ports.Run();
+}
+
+TEST(ExceptionPorts, TaskExceptionPorts_SetInProcess_MainThreadCrash) {
+ TestExceptionPorts test_exception_ports(
+ TestExceptionPorts::kSetOnTaskOnly,
+ TestExceptionPorts::kSetInProcess,
+ TestExceptionPorts::kMainThreadCrashes);
+ test_exception_ports.Run();
+}
+
+TEST(ExceptionPorts, TaskExceptionPorts_SetInProcess_OtherThreadCrash) {
+ TestExceptionPorts test_exception_ports(
+ TestExceptionPorts::kSetOnTaskOnly,
+ TestExceptionPorts::kSetInProcess,
+ TestExceptionPorts::kOtherThreadCrashes);
+ test_exception_ports.Run();
+}
+
+TEST(ExceptionPorts, TaskAndThreadExceptionPorts_SetInProcess_NoCrash) {
+ TestExceptionPorts test_exception_ports(
+ TestExceptionPorts::kSetOnTaskAndThreads,
+ TestExceptionPorts::kSetInProcess,
+ TestExceptionPorts::kNobodyCrashes);
+ test_exception_ports.Run();
+}
+
+TEST(ExceptionPorts, TaskAndThreadExceptionPorts_SetInProcess_MainThreadCrash) {
+ TestExceptionPorts test_exception_ports(
+ TestExceptionPorts::kSetOnTaskAndThreads,
+ TestExceptionPorts::kSetInProcess,
+ TestExceptionPorts::kMainThreadCrashes);
+ test_exception_ports.Run();
+}
+
+TEST(ExceptionPorts,
+ TaskAndThreadExceptionPorts_SetInProcess_OtherThreadCrash) {
+ TestExceptionPorts test_exception_ports(
+ TestExceptionPorts::kSetOnTaskAndThreads,
+ TestExceptionPorts::kSetInProcess,
+ TestExceptionPorts::kOtherThreadCrashes);
+ test_exception_ports.Run();
+}
+
+TEST(ExceptionPorts, TaskExceptionPorts_SetOutOfProcess_NoCrash) {
+ TestExceptionPorts test_exception_ports(
+ TestExceptionPorts::kSetOnTaskOnly,
+ TestExceptionPorts::kSetOutOfProcess,
+ TestExceptionPorts::kNobodyCrashes);
+ test_exception_ports.Run();
+}
+
+TEST(ExceptionPorts, TaskExceptionPorts_SetOutOfProcess_MainThreadCrash) {
+ TestExceptionPorts test_exception_ports(
+ TestExceptionPorts::kSetOnTaskOnly,
+ TestExceptionPorts::kSetOutOfProcess,
+ TestExceptionPorts::kMainThreadCrashes);
+ test_exception_ports.Run();
+}
+
+TEST(ExceptionPorts, TaskExceptionPorts_SetOutOfProcess_OtherThreadCrash) {
+ TestExceptionPorts test_exception_ports(
+ TestExceptionPorts::kSetOnTaskOnly,
+ TestExceptionPorts::kSetOutOfProcess,
+ TestExceptionPorts::kOtherThreadCrashes);
+ test_exception_ports.Run();
+}
+
+TEST(ExceptionPorts, TaskAndThreadExceptionPorts_SetOutOfProcess_NoCrash) {
+ TestExceptionPorts test_exception_ports(
+ TestExceptionPorts::kSetOnTaskAndThreads,
+ TestExceptionPorts::kSetOutOfProcess,
+ TestExceptionPorts::kNobodyCrashes);
+ test_exception_ports.Run();
+}
+
+TEST(ExceptionPorts,
+ TaskAndThreadExceptionPorts_SetOutOfProcess_MainThreadCrash) {
+ TestExceptionPorts test_exception_ports(
+ TestExceptionPorts::kSetOnTaskAndThreads,
+ TestExceptionPorts::kSetOutOfProcess,
+ TestExceptionPorts::kMainThreadCrashes);
+ test_exception_ports.Run();
+}
+
+TEST(ExceptionPorts,
+ TaskAndThreadExceptionPorts_SetOutOfProcess_OtherThreadCrash) {
+ TestExceptionPorts test_exception_ports(
+ TestExceptionPorts::kSetOnTaskAndThreads,
+ TestExceptionPorts::kSetOutOfProcess,
+ TestExceptionPorts::kOtherThreadCrashes);
+ test_exception_ports.Run();
+}
+
+TEST(ExceptionPorts, HostExceptionPorts) {
+ // ExceptionPorts isn’t expected to work as non-root. Just do a quick test to
+ // make sure that TargetTypeName() returns the right string, and that the
+ // underlying host_get_exception_ports() function appears to be called by
+ // looking for a KERN_INVALID_ARGUMENT return value. Or, on the off chance
+ // that the test is being run as root, just look for KERN_SUCCESS.
+ // host_set_exception_ports() is not tested, because if the test were running
+ // as root and the call succeeded, it would have global effects.
+
+ base::mac::ScopedMachSendRight host(mach_host_self());
+ ExceptionPorts explicit_host_ports(ExceptionPorts::kTargetTypeHost, host);
+ EXPECT_EQ("host", explicit_host_ports.TargetTypeName());
+
+ std::vector<ExceptionPorts::ExceptionHandler> handlers;
+ bool rv = explicit_host_ports.GetExceptionPorts(
+ ExcMaskAll() | EXC_MASK_CRASH, &handlers);
+ if (geteuid() == 0) {
+ EXPECT_TRUE(rv);
+ } else {
+ EXPECT_FALSE(rv);
+ }
+
+ ExceptionPorts implicit_host_ports(ExceptionPorts::kTargetTypeHost,
+ HOST_NULL);
+ EXPECT_EQ("host", implicit_host_ports.TargetTypeName());
+
+ rv = implicit_host_ports.GetExceptionPorts(
+ ExcMaskAll() | EXC_MASK_CRASH, &handlers);
+ if (geteuid() == 0) {
+ EXPECT_TRUE(rv);
+ } else {
+ EXPECT_FALSE(rv);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/exception_types.cc b/chromium/third_party/crashpad/crashpad/util/mach/exception_types.cc
new file mode 100644
index 00000000000..0199b3145a7
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/exception_types.cc
@@ -0,0 +1,212 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/exception_types.h"
+
+#include <Availability.h>
+#include <AvailabilityMacros.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <libproc.h>
+#include <kern/exc_resource.h>
+
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "util/mac/mac_util.h"
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
+
+extern "C" {
+
+// proc_get_wakemon_params() is present in the Mac OS X 10.9 SDK, but no
+// declaration is provided. This provides a declaration and marks it for weak
+// import if the deployment target is below 10.9.
+int proc_get_wakemon_params(pid_t pid, int* rate_hz, int* flags)
+ __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0);
+
+// Redeclare the method without the availability annotation to suppress the
+// -Wpartial-availability warning.
+int proc_get_wakemon_params(pid_t pid, int* rate_hz, int* flags);
+
+} // extern "C"
+
+#else
+
+namespace {
+
+using ProcGetWakemonParamsType = int (*)(pid_t, int*, int*);
+
+// The SDK doesn’t have proc_get_wakemon_params() to link against, even with
+// weak import. This function returns a function pointer to it if it exists at
+// runtime, or nullptr if it doesn’t. proc_get_wakemon_params() is looked up in
+// the same module that provides proc_pidinfo().
+ProcGetWakemonParamsType GetProcGetWakemonParams() {
+ Dl_info dl_info;
+ if (!dladdr(reinterpret_cast<void*>(proc_pidinfo), &dl_info)) {
+ return nullptr;
+ }
+
+ void* dl_handle =
+ dlopen(dl_info.dli_fname, RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
+ if (!dl_handle) {
+ return nullptr;
+ }
+
+ ProcGetWakemonParamsType proc_get_wakemon_params =
+ reinterpret_cast<ProcGetWakemonParamsType>(
+ dlsym(dl_handle, "proc_get_wakemon_params"));
+ return proc_get_wakemon_params;
+}
+
+} // namespace
+
+#endif
+
+namespace {
+
+// Wraps proc_get_wakemon_params(), calling it if the system provides it. It’s
+// present on Mac OS X 10.9 and later. If it’s not available, sets errno to
+// ENOSYS and returns -1.
+int ProcGetWakemonParams(pid_t pid, int* rate_hz, int* flags) {
+#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9
+ // proc_get_wakemon_params() isn’t in the SDK. Look it up dynamically.
+ static ProcGetWakemonParamsType proc_get_wakemon_params =
+ GetProcGetWakemonParams();
+#endif
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9
+ // proc_get_wakemon_params() is definitely available if the deployment target
+ // is 10.9 or newer.
+ if (!proc_get_wakemon_params) {
+ errno = ENOSYS;
+ return -1;
+ }
+#endif
+
+ return proc_get_wakemon_params(pid, rate_hz, flags);
+}
+
+} // namespace
+
+namespace crashpad {
+
+exception_type_t ExcCrashRecoverOriginalException(
+ mach_exception_code_t code_0,
+ mach_exception_code_t* original_code_0,
+ int* signal) {
+ // 10.9.4 xnu-2422.110.17/bsd/kern/kern_exit.c proc_prepareexit() sets code[0]
+ // based on the signal value, original exception type, and low 20 bits of the
+ // original code[0] before calling xnu-2422.110.17/osfmk/kern/exception.c
+ // task_exception_notify() to raise an EXC_CRASH.
+ //
+ // The list of core-generating signals (as used in proc_prepareexit()’s call
+ // to hassigprop()) is in 10.9.4 xnu-2422.110.17/bsd/sys/signalvar.h sigprop:
+ // entires with SA_CORE are in the set. These signals are SIGQUIT, SIGILL,
+ // SIGTRAP, SIGABRT, SIGEMT, SIGFPE, SIGBUS, SIGSEGV, and SIGSYS. Processes
+ // killed for code-signing reasons will be killed by SIGKILL and are also
+ // eligible for EXC_CRASH handling, but processes killed by SIGKILL for other
+ // reasons are not.
+ if (signal) {
+ *signal = (code_0 >> 24) & 0xff;
+ }
+
+ if (original_code_0) {
+ *original_code_0 = code_0 & 0xfffff;
+ }
+
+ return (code_0 >> 20) & 0xf;
+}
+
+bool IsExceptionNonfatalResource(exception_type_t exception,
+ mach_exception_code_t code_0,
+ pid_t pid) {
+ if (exception != EXC_RESOURCE) {
+ return false;
+ }
+
+ const int resource_type = EXC_RESOURCE_DECODE_RESOURCE_TYPE(code_0);
+ const int resource_flavor = EXC_RESOURCE_DECODE_FLAVOR(code_0);
+
+ if (resource_type == RESOURCE_TYPE_CPU &&
+ (resource_flavor == FLAVOR_CPU_MONITOR ||
+ resource_flavor == FLAVOR_CPU_MONITOR_FATAL)) {
+ // These exceptions may be fatal. They are not fatal by default at task
+ // creation but can be made fatal by calling proc_rlimit_control() with
+ // RLIMIT_CPU_USAGE_MONITOR as the second argument and CPUMON_MAKE_FATAL set
+ // in the flags.
+ if (MacOSXMinorVersion() >= 10) {
+ // In Mac OS X 10.10, the exception code indicates whether the exception
+ // is fatal. See 10.10 xnu-2782.1.97/osfmk/kern/thread.c
+ // THIS_THREAD_IS_CONSUMING_TOO_MUCH_CPU__SENDING_EXC_RESOURCE().
+ return resource_flavor == FLAVOR_CPU_MONITOR;
+ }
+
+ // In Mac OS X 10.9, there’s no way to determine whether the exception is
+ // fatal. Unlike RESOURCE_TYPE_WAKEUPS below, there’s no way to determine
+ // this outside the kernel. proc_rlimit_control()’s RLIMIT_CPU_USAGE_MONITOR
+ // is the only interface to modify CPUMON_MAKE_FATAL, but it’s only able to
+ // set this bit, not obtain its current value.
+ //
+ // Default to assuming that these exceptions are nonfatal. They are nonfatal
+ // by default and no users of proc_rlimit_control() were found on 10.9.5
+ // 13F1066 in /System and /usr outside of Metadata.framework and associated
+ // tools.
+ return true;
+ }
+
+ if (resource_type == RESOURCE_TYPE_WAKEUPS &&
+ resource_flavor == FLAVOR_WAKEUPS_MONITOR) {
+ // These exceptions may be fatal. They are not fatal by default at task
+ // creation, but can be made fatal by calling proc_rlimit_control() with
+ // RLIMIT_WAKEUPS_MONITOR as the second argument and WAKEMON_MAKE_FATAL set
+ // in the flags.
+ //
+ // proc_get_wakemon_params() (which calls
+ // through to proc_rlimit_control() with RLIMIT_WAKEUPS_MONITOR) determines
+ // whether these exceptions are fatal. See 10.10
+ // xnu-2782.1.97/osfmk/kern/task.c
+ // THIS_PROCESS_IS_CAUSING_TOO_MANY_WAKEUPS__SENDING_EXC_RESOURCE().
+ //
+ // If proc_get_wakemon_params() fails, default to assuming that these
+ // exceptions are nonfatal. They are nonfatal by default and no users of
+ // proc_rlimit_control() were found on 10.9.5 13F1066 in /System and /usr
+ // outside of Metadata.framework and associated tools.
+ int wm_rate;
+ int wm_flags;
+ int rv = ProcGetWakemonParams(pid, &wm_rate, &wm_flags);
+ if (rv < 0) {
+ PLOG(WARNING) << "ProcGetWakemonParams";
+ return true;
+ }
+
+ return !(wm_flags & WAKEMON_MAKE_FATAL);
+ }
+
+ if (resource_type == RESOURCE_TYPE_MEMORY &&
+ resource_flavor == FLAVOR_HIGH_WATERMARK) {
+ // These exceptions are never fatal. See 10.10
+ // xnu-2782.1.97/osfmk/kern/task.c
+ // THIS_PROCESS_CROSSED_HIGH_WATERMARK__SENDING_EXC_RESOURCE().
+ return true;
+ }
+
+ // Treat unknown exceptions as fatal. This is the conservative approach: it
+ // may result in more crash reports being generated, but the type-flavor
+ // combinations can be evaluated to determine appropriate handling.
+ LOG(WARNING) << "unknown resource type " << resource_type << " flavor "
+ << resource_flavor;
+ return false;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/exception_types.h b/chromium/third_party/crashpad/crashpad/util/mach/exception_types.h
new file mode 100644
index 00000000000..834f2123b0d
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/exception_types.h
@@ -0,0 +1,79 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MACH_EXCEPTION_TYPES_H_
+#define CRASHPAD_UTIL_MACH_EXCEPTION_TYPES_H_
+
+#include <mach/mach.h>
+#include <sys/types.h>
+
+namespace crashpad {
+
+//! \brief Recovers the original exception, first exception code, and signal
+//! from the encoded form of the first exception code delivered with
+//! `EXC_CRASH` exceptions.
+//!
+//! `EXC_CRASH` exceptions are generated when the kernel has committed to
+//! terminating a process as a result of a core-generating POSIX signal and, for
+//! hardware exceptions, an earlier Mach exception. Information about this
+//! earlier exception and signal is made available to the `EXC_CRASH` handler
+//! via its `code[0]` parameter. This function recovers the original exception,
+//! the value of `code[0]` from the original exception, and the value of the
+//! signal responsible for process termination.
+//!
+//! \param[in] code_0 The first exception code (`code[0]`) passed to a Mach
+//! exception handler in an `EXC_CRASH` exception. It is invalid to call
+//! this function with an exception code from any exception other than
+//! `EXC_CRASH`.
+//! \param[out] original_code_0 The first exception code (`code[0]`) passed to
+//! the Mach exception handler for a hardware exception that resulted in the
+//! generation of a POSIX signal that caused process termination. If the
+//! signal that caused termination was not sent as a result of a hardware
+//! exception, this will be `0`. Callers that do not need this value may
+//! pass `nullptr`.
+//! \param[out] signal The POSIX signal that caused process termination. Callers
+//! that do not need this value may pass `nullptr`.
+//!
+//! \return The original exception for a hardware exception that resulted in the
+//! generation of a POSIX signal that caused process termination. If the
+//! signal that caused termination was not sent as a result of a hardware
+//! exception, this will be `0`.
+exception_type_t ExcCrashRecoverOriginalException(
+ mach_exception_code_t code_0,
+ mach_exception_code_t* original_code_0,
+ int* signal);
+
+//! \brief Determines whether an exception is a non-fatal `EXC_RESOURCE`.
+//!
+//! \param[in] exception The exception type as received by a Mach exception
+//! handler.
+//! \param[in] code_0 The first exception code (`code[0]`) as received by a
+//! Mach exception handler.
+//! \param[in] pid The process ID that the exception occurred in. In some cases,
+//! process may need to be queried to determine whether an `EXC_RESOURCE`
+//! exception is fatal.
+//!
+//! \return `true` if the exception is a non-fatal `EXC_RESOURCE`. `false`
+//! otherwise. If the exception is `EXC_RESOURCE` of a recognized type but
+//! it is not possible to determine whether it is fatal, returns `true`
+//! under the assumption that all known `EXC_RESOURCE` exceptions are
+//! non-fatal by default. If the exception is not `EXC_RESOURCE` or is an
+//! unknown `EXC_RESOURCE` type, returns `false`.
+bool IsExceptionNonfatalResource(exception_type_t exception,
+ mach_exception_code_t code_0,
+ pid_t pid);
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MACH_EXCEPTION_TYPES_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/exception_types_test.cc b/chromium/third_party/crashpad/crashpad/util/mach/exception_types_test.cc
new file mode 100644
index 00000000000..0520c2e02bb
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/exception_types_test.cc
@@ -0,0 +1,140 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/exception_types.h"
+
+#include <kern/exc_resource.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+#include "util/mac/mac_util.h"
+#include "util/mach/mach_extensions.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(ExceptionTypes, ExcCrashRecoverOriginalException) {
+ struct TestData {
+ mach_exception_code_t code_0;
+ exception_type_t exception;
+ mach_exception_code_t original_code_0;
+ int signal;
+ };
+ const TestData kTestData[] = {
+ {0xb100001, EXC_BAD_ACCESS, KERN_INVALID_ADDRESS, SIGSEGV},
+ {0xb100002, EXC_BAD_ACCESS, KERN_PROTECTION_FAILURE, SIGSEGV},
+ {0xa100002, EXC_BAD_ACCESS, KERN_PROTECTION_FAILURE, SIGBUS},
+ {0x4200001, EXC_BAD_INSTRUCTION, 1, SIGILL},
+ {0x8300001, EXC_ARITHMETIC, 1, SIGFPE},
+ {0x5600002, EXC_BREAKPOINT, 2, SIGTRAP},
+ {0x3000000, 0, 0, SIGQUIT},
+ {0x6000000, 0, 0, SIGABRT},
+ {0xc000000, 0, 0, SIGSYS},
+ {0, 0, 0, 0},
+ };
+
+ for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ const TestData& test_data = kTestData[index];
+ SCOPED_TRACE(base::StringPrintf(
+ "index %zu, code_0 0x%llx", index, test_data.code_0));
+
+ mach_exception_code_t original_code_0;
+ int signal;
+ exception_type_t exception = ExcCrashRecoverOriginalException(
+ test_data.code_0, &original_code_0, &signal);
+
+ EXPECT_EQ(test_data.exception, exception);
+ EXPECT_EQ(test_data.original_code_0, original_code_0);
+ EXPECT_EQ(test_data.signal, signal);
+ }
+
+ // Now make sure that ExcCrashRecoverOriginalException() properly ignores
+ // optional arguments.
+ static_assert(arraysize(kTestData) >= 1, "must have something to test");
+ const TestData& test_data = kTestData[0];
+ EXPECT_EQ(
+ test_data.exception,
+ ExcCrashRecoverOriginalException(test_data.code_0, nullptr, nullptr));
+
+ mach_exception_code_t original_code_0;
+ EXPECT_EQ(test_data.exception,
+ ExcCrashRecoverOriginalException(
+ test_data.code_0, &original_code_0, nullptr));
+ EXPECT_EQ(test_data.original_code_0, original_code_0);
+
+ int signal;
+ EXPECT_EQ(
+ test_data.exception,
+ ExcCrashRecoverOriginalException(test_data.code_0, nullptr, &signal));
+ EXPECT_EQ(test_data.signal, signal);
+}
+
+// These macros come from the #ifdef KERNEL section of <kern/exc_resource.h>:
+// 10.10 xnu-2782.1.97/osfmk/kern/exc_resource.h.
+#define EXC_RESOURCE_ENCODE_TYPE(code, type) \
+ ((code) |= ((static_cast<uint64_t>(type) & 0x7ull) << 61))
+#define EXC_RESOURCE_ENCODE_FLAVOR(code, flavor) \
+ ((code) |= ((static_cast<uint64_t>(flavor) & 0x7ull) << 58))
+
+TEST(ExceptionTypes, IsExceptionNonfatalResource) {
+ const pid_t pid = getpid();
+
+ mach_exception_code_t code = 0;
+ EXC_RESOURCE_ENCODE_TYPE(code, RESOURCE_TYPE_CPU);
+ EXC_RESOURCE_ENCODE_FLAVOR(code, FLAVOR_CPU_MONITOR);
+ EXPECT_TRUE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid));
+
+ if (MacOSXMinorVersion() >= 10) {
+ // FLAVOR_CPU_MONITOR_FATAL was introduced in Mac OS X 10.10.
+ code = 0;
+ EXC_RESOURCE_ENCODE_TYPE(code, RESOURCE_TYPE_CPU);
+ EXC_RESOURCE_ENCODE_FLAVOR(code, FLAVOR_CPU_MONITOR_FATAL);
+ EXPECT_FALSE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid));
+ }
+
+ // This assumes that WAKEMON_MAKE_FATAL is not set for this process. The
+ // default is for WAKEMON_MAKE_FATAL to not be set, there’s no public API to
+ // enable it, and nothing in this process should have enabled it.
+ code = 0;
+ EXC_RESOURCE_ENCODE_TYPE(code, RESOURCE_TYPE_WAKEUPS);
+ EXC_RESOURCE_ENCODE_FLAVOR(code, FLAVOR_WAKEUPS_MONITOR);
+ EXPECT_TRUE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid));
+
+ code = 0;
+ EXC_RESOURCE_ENCODE_TYPE(code, RESOURCE_TYPE_MEMORY);
+ EXC_RESOURCE_ENCODE_FLAVOR(code, FLAVOR_HIGH_WATERMARK);
+ EXPECT_TRUE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid));
+
+ // Non-EXC_RESOURCE exceptions should never be considered nonfatal resource
+ // exceptions, because they aren’t resource exceptions at all.
+ EXPECT_FALSE(IsExceptionNonfatalResource(EXC_CRASH, 0xb100001, pid));
+ EXPECT_FALSE(IsExceptionNonfatalResource(EXC_CRASH, 0x0b00000, pid));
+ EXPECT_FALSE(IsExceptionNonfatalResource(EXC_CRASH, 0x6000000, pid));
+ EXPECT_FALSE(
+ IsExceptionNonfatalResource(EXC_BAD_ACCESS, KERN_INVALID_ADDRESS, pid));
+ EXPECT_FALSE(IsExceptionNonfatalResource(EXC_BAD_INSTRUCTION, 1, pid));
+ EXPECT_FALSE(IsExceptionNonfatalResource(EXC_ARITHMETIC, 1, pid));
+ EXPECT_FALSE(IsExceptionNonfatalResource(EXC_BREAKPOINT, 2, pid));
+ EXPECT_FALSE(IsExceptionNonfatalResource(0, 0, pid));
+ EXPECT_FALSE(IsExceptionNonfatalResource(kMachExceptionSimulated, 0, pid));
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/mach_extensions.cc b/chromium/third_party/crashpad/crashpad/util/mach/mach_extensions.cc
new file mode 100644
index 00000000000..63aae31d9ce
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/mach_extensions.cc
@@ -0,0 +1,79 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/mach_extensions.h"
+
+#include <AvailabilityMacros.h>
+#include <pthread.h>
+
+#include "base/mac/mach_logging.h"
+#include "util/mac/mac_util.h"
+
+namespace crashpad {
+
+thread_t MachThreadSelf() {
+ // The pthreads library keeps its own copy of the thread port. Using it does
+ // not increment its reference count.
+ return pthread_mach_thread_np(pthread_self());
+}
+
+mach_port_t NewMachPort(mach_port_right_t right) {
+ mach_port_t port = MACH_PORT_NULL;
+ kern_return_t kr = mach_port_allocate(mach_task_self(), right, &port);
+ MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_port_allocate";
+ return port;
+}
+
+exception_mask_t ExcMaskAll() {
+ // This is necessary because of the way that the kernel validates
+ // exception_mask_t arguments to
+ // {host,task,thread}_{get,set,swap}_exception_ports(). It is strict,
+ // rejecting attempts to operate on any bits that it does not recognize. See
+ // 10.9.4 xnu-2422.110.17/osfmk/mach/ipc_host.c and
+ // xnu-2422.110.17/osfmk/mach/ipc_tt.c.
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9
+ int mac_os_x_minor_version = MacOSXMinorVersion();
+#endif
+
+ // See 10.6.8 xnu-1504.15.3/osfmk/mach/exception_types.h. 10.7 uses the same
+ // definition as 10.6. See 10.7.5 xnu-1699.32.7/osfmk/mach/exception_types.h
+ const exception_mask_t kExcMaskAll_10_6 =
+ EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC |
+ EXC_MASK_EMULATION | EXC_MASK_SOFTWARE | EXC_MASK_BREAKPOINT |
+ EXC_MASK_SYSCALL | EXC_MASK_MACH_SYSCALL | EXC_MASK_RPC_ALERT |
+ EXC_MASK_MACHINE;
+#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_7
+ if (mac_os_x_minor_version <= 7) {
+ return kExcMaskAll_10_6;
+ }
+#endif
+
+ // 10.8 added EXC_MASK_RESOURCE. See 10.8.5
+ // xnu-2050.48.11/osfmk/mach/exception_types.h.
+ const exception_mask_t kExcMaskAll_10_8 =
+ kExcMaskAll_10_6 | EXC_MASK_RESOURCE;
+#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_8
+ if (mac_os_x_minor_version <= 8) {
+ return kExcMaskAll_10_8;
+ }
+#endif
+
+ // 10.9 added EXC_MASK_GUARD. See 10.9.4
+ // xnu-2422.110.17/osfmk/mach/exception_types.h.
+ const exception_mask_t kExcMaskAll_10_9 = kExcMaskAll_10_8 | EXC_MASK_GUARD;
+ return kExcMaskAll_10_9;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/mach_extensions.h b/chromium/third_party/crashpad/crashpad/util/mach/mach_extensions.h
new file mode 100644
index 00000000000..d1e392ad7a5
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/mach_extensions.h
@@ -0,0 +1,104 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MACH_MACH_EXTENSIONS_H_
+#define CRASHPAD_UTIL_MACH_MACH_EXTENSIONS_H_
+
+#include <mach/mach.h>
+
+namespace crashpad {
+
+//! \brief `MACH_PORT_NULL` with the correct type for a Mach port,
+//! `mach_port_t`.
+//!
+//! For situations where implicit conversions between signed and unsigned types
+//! are not performed, use kMachPortNull instead of an explicit `implicit_cast`
+//! of `MACH_PORT_NULL` to `mach_port_t`. This is useful for logging and testing
+//! assertions.
+const mach_port_t kMachPortNull = MACH_PORT_NULL;
+
+//! \brief `MACH_EXCEPTION_CODES` with the correct type for a Mach exception
+//! behavior, `exception_behavior_t`.
+//!
+//! Signedness problems can occur when ORing `MACH_EXCEPTION_CODES` as a signed
+//! integer, because a signed integer overflow results. This constant can be
+//! used instead of `MACH_EXCEPTION_CODES` in such cases.
+const exception_behavior_t kMachExceptionCodes = MACH_EXCEPTION_CODES;
+
+// Because exception_mask_t is an int and has one bit for each defined
+// exception_type_t, it’s reasonable to assume that there cannot be any
+// officially-defined exception_type_t values higher than 31.
+// kMachExceptionSimulated uses a value well outside this range because it does
+// not require a corresponding mask value. Simulated exceptions are delivered to
+// the exception handler registered for EXC_CRASH exceptions using
+// EXC_MASK_CRASH.
+
+//! \brief An exception type to use for simulated exceptions.
+const exception_type_t kMachExceptionSimulated = 'CPsx';
+
+//! \brief A const version of `thread_state_t`.
+//!
+//! This is useful as the \a old_state parameter to exception handler functions.
+//! Normally, these parameters are of type `thread_state_t`, but this allows
+//! modification of the state, which is conceptually `const`.
+using ConstThreadState = const natural_t*;
+
+//! \brief Like `mach_thread_self()`, but without the obligation to release the
+//! send right.
+//!
+//! `mach_thread_self()` returns a send right to the current thread port,
+//! incrementing its reference count. This burdens the caller with maintaining
+//! this send right, and calling `mach_port_deallocate()` when it is no longer
+//! needed. This is burdensome, and is at odds with the normal operation of
+//! `mach_task_self()`, which does not increment the task port’s reference count
+//! whose result must not be deallocated.
+//!
+//! Callers can use this function in preference to `mach_thread_self()`. This
+//! function returns an extant reference to the current thread’s port without
+//! incrementing its reference count.
+//!
+//! \return The value of `mach_thread_self()` without incrementing its reference
+//! count. The returned port must not be deallocated by
+//! `mach_port_deallocate()`. The returned value is valid as long as the
+//! thread continues to exist as a `pthread_t`.
+thread_t MachThreadSelf();
+
+//! \brief Creates a new Mach port in the current task.
+//!
+//! This function wraps the `mach_port_allocate()` providing a simpler
+//! interface.
+//!
+//! \param[in] right The type of right to create.
+//!
+//! \return The new Mach port. On failure, `MACH_PORT_NULL` with a message
+//! logged.
+mach_port_t NewMachPort(mach_port_right_t right);
+
+//! \brief The value for `EXC_MASK_ALL` appropriate for the operating system at
+//! run time.
+//!
+//! The SDK’s definition of `EXC_MASK_ALL` has changed over time, with later
+//! versions containing more bits set than earlier versions. However, older
+//! kernels will reject exception masks that contain bits set that they don’t
+//! recognize. Calling this function will return a value for `EXC_MASK_ALL`
+//! appropriate for the system at run time.
+//!
+//! \note `EXC_MASK_ALL` does not include the value of `EXC_MASK_CRASH`.
+//! Consumers that want `EXC_MASK_ALL` along with `EXC_MASK_CRASH` must use
+//! ExcMaskAll() | `EXC_MASK_CRASH` explicitly.
+exception_mask_t ExcMaskAll();
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MACH_MACH_EXTENSIONS_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/mach_extensions_test.cc b/chromium/third_party/crashpad/crashpad/util/mach/mach_extensions_test.cc
new file mode 100644
index 00000000000..aefe489d1bf
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/mach_extensions_test.cc
@@ -0,0 +1,65 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/mach_extensions.h"
+
+#include "base/mac/scoped_mach_port.h"
+#include "gtest/gtest.h"
+#include "test/mac/mach_errors.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(MachExtensions, MachThreadSelf) {
+ base::mac::ScopedMachSendRight thread_self(mach_thread_self());
+ EXPECT_EQ(thread_self, MachThreadSelf());
+}
+
+TEST(MachExtensions, NewMachPort_Receive) {
+ base::mac::ScopedMachReceiveRight port(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
+ ASSERT_NE(kMachPortNull, port);
+
+ mach_port_type_t type;
+ kern_return_t kr = mach_port_type(mach_task_self(), port, &type);
+ ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_get_type");
+
+ EXPECT_EQ(MACH_PORT_TYPE_RECEIVE, type);
+}
+
+TEST(MachExtensions, NewMachPort_PortSet) {
+ base::mac::ScopedMachPortSet port(NewMachPort(MACH_PORT_RIGHT_PORT_SET));
+ ASSERT_NE(kMachPortNull, port);
+
+ mach_port_type_t type;
+ kern_return_t kr = mach_port_type(mach_task_self(), port, &type);
+ ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_get_type");
+
+ EXPECT_EQ(MACH_PORT_TYPE_PORT_SET, type);
+}
+
+TEST(MachExtensions, NewMachPort_DeadName) {
+ base::mac::ScopedMachSendRight port(NewMachPort(MACH_PORT_RIGHT_DEAD_NAME));
+ ASSERT_NE(kMachPortNull, port);
+
+ mach_port_type_t type;
+ kern_return_t kr = mach_port_type(mach_task_self(), port, &type);
+ ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_get_type");
+
+ EXPECT_EQ(MACH_PORT_TYPE_DEAD_NAME, type);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/mach_message.cc b/chromium/third_party/crashpad/crashpad/util/mach/mach_message.cc
new file mode 100644
index 00000000000..8092f26c117
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/mach_message.cc
@@ -0,0 +1,252 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/mach_message.h"
+
+#include <AvailabilityMacros.h>
+#include <bsm/libbsm.h>
+
+#include <limits>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "util/misc/clock.h"
+
+namespace crashpad {
+
+namespace {
+
+const int kNanosecondsPerMillisecond = 1E6;
+
+// TimerRunning() determines whether |deadline| has passed. If |deadline| is
+// kMachMessageDeadlineWaitIndefinitely, |*timeout_options| is set to
+// MACH_MSG_OPTION_NONE, |*remaining_ms| is set to MACH_MSG_TIMEOUT_NONE, and
+// this function returns true. When used with mach_msg(), this will cause
+// indefinite waiting. In any other case, |*timeout_options| is set to
+// MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT, so mach_msg() will enforce a timeout
+// specified by |*remaining_ms|. If |deadline| is in the future, |*remaining_ms|
+// is set to the number of milliseconds remaining, which will always be a
+// positive value, and this function returns true. If |deadline| is
+// kMachMessageDeadlineNonblocking (indicating that no timer is in effect),
+// |*remaining_ms| is set to zero and this function returns true. Otherwise,
+// this function sets |*remaining_ms| to zero and returns false.
+bool TimerRunning(uint64_t deadline,
+ mach_msg_timeout_t* remaining_ms,
+ mach_msg_option_t* timeout_options) {
+ if (deadline == kMachMessageDeadlineWaitIndefinitely) {
+ *remaining_ms = MACH_MSG_TIMEOUT_NONE;
+ *timeout_options = MACH_MSG_OPTION_NONE;
+ return true;
+ }
+
+ *timeout_options = MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT;
+
+ if (deadline == kMachMessageDeadlineNonblocking) {
+ *remaining_ms = 0;
+ return true;
+ }
+
+ uint64_t now = ClockMonotonicNanoseconds();
+
+ if (now >= deadline) {
+ *remaining_ms = 0;
+ } else {
+ uint64_t remaining = deadline - now;
+
+ // Round to the nearest millisecond, taking care not to overflow.
+ const int kHalfMillisecondInNanoseconds = kNanosecondsPerMillisecond / 2;
+ if (remaining <=
+ std::numeric_limits<uint64_t>::max() - kHalfMillisecondInNanoseconds) {
+ *remaining_ms = (remaining + kHalfMillisecondInNanoseconds) /
+ kNanosecondsPerMillisecond;
+ } else {
+ *remaining_ms = remaining / kNanosecondsPerMillisecond;
+ }
+ }
+
+ return *remaining_ms != 0;
+}
+
+// This is an internal implementation detail of MachMessageWithDeadline(). It
+// determines whether |deadline| has expired, and what timeout value and
+// timeout-related options to pass to mach_msg() based on the value of
+// |deadline|. mach_msg() will only be called if TimerRunning() returns true or
+// if run_even_if_expired is true.
+mach_msg_return_t MachMessageWithDeadlineInternal(mach_msg_header_t* message,
+ mach_msg_option_t options,
+ mach_msg_size_t receive_size,
+ mach_port_name_t receive_port,
+ MachMessageDeadline deadline,
+ mach_port_name_t notify_port,
+ bool run_even_if_expired) {
+ mach_msg_timeout_t remaining_ms;
+ mach_msg_option_t timeout_options;
+ if (!TimerRunning(deadline, &remaining_ms, &timeout_options) &&
+ !run_even_if_expired) {
+ // Simulate the timed-out return values from mach_msg().
+ if (options & MACH_SEND_MSG) {
+ return MACH_SEND_TIMED_OUT;
+ }
+ if (options & MACH_RCV_MSG) {
+ return MACH_RCV_TIMED_OUT;
+ }
+ return MACH_MSG_SUCCESS;
+ }
+
+ // Turn off the passed-in timeout bits and replace them with the ones from
+ // TimerRunning(). Get the send_size value from message->msgh_size if sending
+ // a message.
+ return mach_msg(
+ message,
+ (options & ~(MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT)) | timeout_options,
+ options & MACH_SEND_MSG ? message->msgh_size : 0,
+ receive_size,
+ receive_port,
+ remaining_ms,
+ notify_port);
+}
+
+} // namespace
+
+MachMessageDeadline MachMessageDeadlineFromTimeout(
+ mach_msg_timeout_t timeout_ms) {
+ switch (timeout_ms) {
+ case kMachMessageTimeoutNonblocking:
+ return kMachMessageDeadlineNonblocking;
+ case kMachMessageTimeoutWaitIndefinitely:
+ return kMachMessageDeadlineWaitIndefinitely;
+ default:
+ return ClockMonotonicNanoseconds() +
+ implicit_cast<uint64_t>(timeout_ms) * kNanosecondsPerMillisecond;
+ }
+}
+
+mach_msg_return_t MachMessageWithDeadline(mach_msg_header_t* message,
+ mach_msg_option_t options,
+ mach_msg_size_t receive_size,
+ mach_port_name_t receive_port,
+ MachMessageDeadline deadline,
+ mach_port_name_t notify_port,
+ bool run_even_if_expired) {
+ // mach_msg() actaully does return MACH_MSG_SUCCESS when not asked to send or
+ // receive anything. See 10.9.5 xnu-1504.15.3/osfmk/ipc/mach_msg.c
+ // mach_msg_overwrite_trap().
+ mach_msg_return_t mr = MACH_MSG_SUCCESS;
+
+ // Break up the send and receive into separate operations, so that the timeout
+ // can be recomputed from the deadline for each. Otherwise, the computed
+ // timeout will apply individually to the send and then to the receive, and
+ // the desired deadline could be exceeded.
+ //
+ // During sends, always set MACH_SEND_INTERRUPT, and during receives, always
+ // set MACH_RCV_INTERRUPT. If the caller didn’t specify these options, the
+ // calls will be retried with a recomputed deadline. If these bits weren’t
+ // set, the libsyscall wrapper (10.9.5
+ // xnu-2422.115.4/libsyscall/mach/mach_msg.c mach_msg() would restart
+ // interrupted calls with the original timeout value computed from the
+ // deadline, which would no longer correspond to the actual deadline. If the
+ // caller did specify these bits, don’t restart anything, because the caller
+ // wants to be notified of any interrupted calls.
+
+ if (options & MACH_SEND_MSG) {
+ do {
+ mr = MachMessageWithDeadlineInternal(
+ message,
+ (options & ~MACH_RCV_MSG) | MACH_SEND_INTERRUPT,
+ 0,
+ MACH_PORT_NULL,
+ deadline,
+ notify_port,
+ run_even_if_expired);
+ } while (mr == MACH_SEND_INTERRUPTED && !(options & MACH_SEND_INTERRUPT));
+
+ if (mr != MACH_MSG_SUCCESS) {
+ return mr;
+ }
+ }
+
+ if (options & MACH_RCV_MSG) {
+ do {
+ mr = MachMessageWithDeadlineInternal(
+ message,
+ (options & ~MACH_SEND_MSG) | MACH_RCV_INTERRUPT,
+ receive_size,
+ receive_port,
+ deadline,
+ notify_port,
+ run_even_if_expired);
+ } while (mr == MACH_RCV_INTERRUPTED && !(options & MACH_RCV_INTERRUPT));
+ }
+
+ return mr;
+}
+
+void PrepareMIGReplyFromRequest(const mach_msg_header_t* in_header,
+ mach_msg_header_t* out_header) {
+ out_header->msgh_bits =
+ MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(in_header->msgh_bits), 0);
+ out_header->msgh_remote_port = in_header->msgh_remote_port;
+ out_header->msgh_size = sizeof(mig_reply_error_t);
+ out_header->msgh_local_port = MACH_PORT_NULL;
+ out_header->msgh_id = in_header->msgh_id + 100;
+ reinterpret_cast<mig_reply_error_t*>(out_header)->NDR = NDR_record;
+
+ // MIG-generated dispatch routines don’t do this, but they should.
+ out_header->msgh_reserved = 0;
+}
+
+void SetMIGReplyError(mach_msg_header_t* out_header, kern_return_t error) {
+ reinterpret_cast<mig_reply_error_t*>(out_header)->RetCode = error;
+}
+
+const mach_msg_trailer_t* MachMessageTrailerFromHeader(
+ const mach_msg_header_t* header) {
+ vm_address_t header_address = reinterpret_cast<vm_address_t>(header);
+ vm_address_t trailer_address = header_address + round_msg(header->msgh_size);
+ return reinterpret_cast<const mach_msg_trailer_t*>(trailer_address);
+}
+
+pid_t AuditPIDFromMachMessageTrailer(const mach_msg_trailer_t* trailer) {
+ if (trailer->msgh_trailer_type != MACH_MSG_TRAILER_FORMAT_0) {
+ LOG(ERROR) << "unexpected msgh_trailer_type " << trailer->msgh_trailer_type;
+ return -1;
+ }
+ if (trailer->msgh_trailer_size <
+ REQUESTED_TRAILER_SIZE(kMachMessageReceiveAuditTrailer)) {
+ LOG(ERROR) << "small msgh_trailer_size " << trailer->msgh_trailer_size;
+ return -1;
+ }
+
+ const mach_msg_audit_trailer_t* audit_trailer =
+ reinterpret_cast<const mach_msg_audit_trailer_t*>(trailer);
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8
+ pid_t audit_pid;
+ audit_token_to_au32(audit_trailer->msgh_audit,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ &audit_pid,
+ nullptr,
+ nullptr);
+#else
+ pid_t audit_pid = audit_token_to_pid(audit_trailer->msgh_audit);
+#endif
+
+ return audit_pid;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/mach_message.h b/chromium/third_party/crashpad/crashpad/util/mach/mach_message.h
new file mode 100644
index 00000000000..be882c17e0f
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/mach_message.h
@@ -0,0 +1,179 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MACH_MACH_MESSAGE_H_
+#define CRASHPAD_UTIL_MACH_MACH_MESSAGE_H_
+
+#include <mach/mach.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+namespace crashpad {
+
+//! \brief A Mach message option specifying that an audit trailer should be
+//! delivered during a receive operation.
+//!
+//! This constant is provided because the macros normally used to request this
+//! behavior are cumbersome.
+const mach_msg_option_t kMachMessageReceiveAuditTrailer =
+ MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
+ MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
+
+//! \brief Special constants used as `mach_msg_timeout_t` values.
+enum : mach_msg_timeout_t {
+ //! \brief When passed to MachMessageDeadlineFromTimeout(), that function will
+ //! return #kMachMessageDeadlineNonblocking.
+ kMachMessageTimeoutNonblocking = 0,
+
+ //! \brief When passed to MachMessageDeadlineFromTimeout(), that function will
+ //! return #kMachMessageDeadlineWaitIndefinitely.
+ kMachMessageTimeoutWaitIndefinitely = 0xffffffff,
+};
+
+//! \brief The time before which a MachMessageWithDeadline() call should
+//! complete.
+//!
+//! A value of this type may be one of the special constants
+//! #kMachMessageDeadlineNonblocking or #kMachMessageDeadlineWaitIndefinitely.
+//! Any other values should be produced by calling
+//! MachMessageDeadlineFromTimeout().
+//!
+//! Internally, these are currently specified on the same time base as
+//! ClockMonotonicNanoseconds(), although this is an implementation detail.
+using MachMessageDeadline = uint64_t;
+
+//! \brief Special constants used as \ref MachMessageDeadline values.
+enum : MachMessageDeadline {
+ //! \brief MachMessageWithDeadline() should not block at all in its operation.
+ kMachMessageDeadlineNonblocking = 0,
+
+ //! \brief MachMessageWithDeadline() should wait indefinitely for the
+ //! requested operation to complete.
+ kMachMessageDeadlineWaitIndefinitely = 0xffffffffffffffff,
+};
+
+//! \brief Computes the deadline for a specified timeout value.
+//!
+//! While deadlines exist on an absolute time scale, timeouts are relative. This
+//! function calculates the deadline as \a timeout_ms milliseconds after it
+//! executes.
+//!
+//! If \a timeout_ms is #kMachMessageDeadlineNonblocking, this function will
+//! return #kMachMessageDeadlineNonblocking. If \a timeout_ms is
+//! #kMachMessageTimeoutWaitIndefinitely, this function will return
+//! #kMachMessageDeadlineWaitIndefinitely.
+MachMessageDeadline MachMessageDeadlineFromTimeout(
+ mach_msg_timeout_t timeout_ms);
+
+//! \brief Runs `mach_msg()` with a deadline, as opposed to a timeout.
+//!
+//! This function is similar to `mach_msg()`, with the following differences:
+//! - The `timeout` parameter has been replaced by \a deadline. The deadline
+//! applies uniformly to a call that is requested to both send and receive
+//! a message.
+//! - The `MACH_SEND_TIMEOUT` and `MACH_RCV_TIMEOUT` bits in \a options are
+//! not used. Timeouts are specified by the \a deadline argument.
+//! - The `send_size` parameter has been removed. Its value is implied by
+//! \a message when \a options contains `MACH_SEND_MSG`.
+//! - The \a run_even_if_expired parameter has been added.
+//!
+//! Like the `mach_msg()` wrapper in `libsyscall`, this function will retry
+//! operations when experiencing `MACH_SEND_INTERRUPTED` and
+//! `MACH_RCV_INTERRUPTED`, unless \a options contains `MACH_SEND_INTERRUPT` or
+//! `MACH_RCV_INTERRUPT`. Unlike `mach_msg()`, which restarts the call with the
+//! full timeout when this occurs, this function continues enforcing the
+//! user-specified \a deadline.
+//!
+//! Except as noted, the parameters and return value are identical to those of
+//! `mach_msg()`.
+//!
+//! \param[in] deadline The time by which this call should complete. If the
+//! deadline is exceeded, this call will return `MACH_SEND_TIMED_OUT` or
+//! `MACH_RCV_TIMED_OUT`.
+//! \param[in] run_even_if_expired If `true`, a deadline that is expired when
+//! this function is called will be treated as though a deadline of
+//! #kMachMessageDeadlineNonblocking had been specified. When `false`, an
+//! expired deadline will result in a `MACH_SEND_TIMED_OUT` or
+//! `MACH_RCV_TIMED_OUT` return value, even if the deadline is already
+//! expired when the function is called.
+mach_msg_return_t MachMessageWithDeadline(mach_msg_header_t* message,
+ mach_msg_option_t options,
+ mach_msg_size_t receive_size,
+ mach_port_name_t receive_port,
+ MachMessageDeadline deadline,
+ mach_port_name_t notify_port,
+ bool run_even_if_expired);
+
+//! \brief Initializes a reply message for a MIG server routine based on its
+//! corresponding request.
+//!
+//! If a request is handled by a server routine, it may be necessary to revise
+//! some of the fields set by this function, such as `msgh_size` and any fields
+//! defined in a routine’s reply structure type.
+//!
+//! \param[in] in_header The request message to base the reply on.
+//! \param[out] out_header The reply message to initialize. \a out_header will
+//! be treated as a `mig_reply_error_t*` and all of its fields will be set
+//! except for `RetCode`, which must be set by SetMIGReplyError(). This
+//! argument is accepted as a `mach_msg_header_t*` instead of a
+//! `mig_reply_error_t*` because that is the type that callers are expected
+//! to possess in the C API.
+void PrepareMIGReplyFromRequest(const mach_msg_header_t* in_header,
+ mach_msg_header_t* out_header);
+
+//! \brief Sets the error code in a reply message for a MIG server routine.
+//!
+//! \param[inout] out_header The reply message to operate on. \a out_header will
+//! be treated as a `mig_reply_error_t*` and its `RetCode` field will be
+//! set. This argument is accepted as a `mach_msg_header_t*` instead of a
+//! `mig_reply_error_t*` because that is the type that callers are expected
+//! to possess in the C API.
+//! \param[in] error The error code to store in \a out_header.
+//!
+//! \sa PrepareMIGReplyFromRequest()
+void SetMIGReplyError(mach_msg_header_t* out_header, kern_return_t error);
+
+//! \brief Returns a Mach message trailer for a message that has been received.
+//!
+//! This function must only be called on Mach messages that have been received
+//! via the Mach messaging interface, such as `mach_msg()`. Messages constructed
+//! for sending do not contain trailers.
+//!
+//! \param[in] header A pointer to a received Mach message.
+//!
+//! \return A pointer to the trailer following the received Mach message’s body.
+//! The contents of the trailer depend on the options provided to
+//! `mach_msg()` or a similar function when the message was received.
+const mach_msg_trailer_t* MachMessageTrailerFromHeader(
+ const mach_msg_header_t* header);
+
+//! \brief Returns the process ID of a Mach message’s sender from its audit
+//! trailer.
+//!
+//! For the audit trailer to be present, the message must have been received
+//! with #kMachMessageReceiveAuditTrailer or its macro equivalent specified in
+//! the receive options.
+//!
+//! If the kernel is the message’s sender, a process ID of `0` will be returned.
+//!
+//! \param[in] trailer The trailer received with a Mach message.
+//!
+//! \return The process ID of the message’s sender, or `-1` on failure with a
+//! message logged. It is considered a failure for \a trailer to not contain
+//! audit information.
+pid_t AuditPIDFromMachMessageTrailer(const mach_msg_trailer_t* trailer);
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MACH_MACH_MESSAGE_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/mach_message_server.cc b/chromium/third_party/crashpad/crashpad/util/mach/mach_message_server.cc
new file mode 100644
index 00000000000..0af19018430
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/mach_message_server.cc
@@ -0,0 +1,280 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/mach_message_server.h"
+
+#include <string.h>
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "base/mac/scoped_mach_vm.h"
+#include "util/mach/mach_message.h"
+
+namespace crashpad {
+
+namespace {
+
+//! \brief Manages a dynamically-allocated buffer to be used for Mach messaging.
+class MachMessageBuffer {
+ public:
+ MachMessageBuffer() : vm_() {}
+
+ ~MachMessageBuffer() {}
+
+ //! \return A pointer to the buffer.
+ mach_msg_header_t* Header() const {
+ return reinterpret_cast<mach_msg_header_t*>(vm_.address());
+ }
+
+ //! \brief Ensures that this object has a buffer of exactly \a size bytes
+ //! available.
+ //!
+ //! If the existing buffer is a different size, it will be reallocated without
+ //! copying any of the old buffer’s contents to the new buffer. The contents
+ //! of the buffer are unspecified after this call, even if no reallocation is
+ //! performed.
+ kern_return_t Reallocate(vm_size_t size) {
+ // This test uses == instead of > so that a large reallocation to receive a
+ // large message doesn’t cause permanent memory bloat for the duration of
+ // a MachMessageServer::Run() loop.
+ if (size != vm_.size()) {
+ // reset() first, so that two allocations don’t exist simultaneously.
+ vm_.reset();
+
+ if (size) {
+ vm_address_t address;
+ kern_return_t kr =
+ vm_allocate(mach_task_self(),
+ &address,
+ size,
+ VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG));
+ if (kr != KERN_SUCCESS) {
+ return kr;
+ }
+
+ vm_.reset(address, size);
+ }
+ }
+
+#if !defined(NDEBUG)
+ // Regardless of whether the allocation was changed, scribble over the
+ // memory to make sure that nothing relies on zero-initialization or stale
+ // contents.
+ memset(Header(), 0x66, size);
+#endif
+
+ return KERN_SUCCESS;
+ }
+
+ private:
+ base::mac::ScopedMachVM vm_;
+
+ DISALLOW_COPY_AND_ASSIGN(MachMessageBuffer);
+};
+
+// Wraps MachMessageWithDeadline(), using a MachMessageBuffer argument which
+// will be resized to |receive_size| (after being page-rounded). MACH_RCV_MSG
+// is always combined into |options|.
+mach_msg_return_t MachMessageAllocateReceive(MachMessageBuffer* request,
+ mach_msg_option_t options,
+ mach_msg_size_t receive_size,
+ mach_port_name_t receive_port,
+ MachMessageDeadline deadline,
+ mach_port_name_t notify_port,
+ bool run_even_if_expired) {
+ mach_msg_size_t request_alloc = round_page(receive_size);
+ kern_return_t kr = request->Reallocate(request_alloc);
+ if (kr != KERN_SUCCESS) {
+ return kr;
+ }
+
+ return MachMessageWithDeadline(request->Header(),
+ options | MACH_RCV_MSG,
+ receive_size,
+ receive_port,
+ deadline,
+ notify_port,
+ run_even_if_expired);
+}
+
+} // namespace
+
+// This method implements a server similar to 10.9.4
+// xnu-2422.110.17/libsyscall/mach/mach_msg.c mach_msg_server_once(). The server
+// callback function and |max_size| parameter have been replaced with a C++
+// interface. The |persistent| parameter has been added, allowing this method to
+// serve as a stand-in for mach_msg_server(). The |timeout_ms| parameter has
+// been added, allowing this function to not block indefinitely.
+//
+// static
+mach_msg_return_t MachMessageServer::Run(Interface* interface,
+ mach_port_t receive_port,
+ mach_msg_options_t options,
+ Persistent persistent,
+ ReceiveLarge receive_large,
+ mach_msg_timeout_t timeout_ms) {
+ options &= ~(MACH_RCV_MSG | MACH_SEND_MSG);
+
+ const MachMessageDeadline deadline =
+ MachMessageDeadlineFromTimeout(timeout_ms);
+
+ if (receive_large == kReceiveLargeResize) {
+ options |= MACH_RCV_LARGE;
+ } else {
+ options &= ~MACH_RCV_LARGE;
+ }
+
+ const mach_msg_size_t trailer_alloc = REQUESTED_TRAILER_SIZE(options);
+ const mach_msg_size_t expected_receive_size =
+ round_msg(interface->MachMessageServerRequestSize()) + trailer_alloc;
+ const mach_msg_size_t request_size = (receive_large == kReceiveLargeResize)
+ ? round_page(expected_receive_size)
+ : expected_receive_size;
+ DCHECK_GE(request_size, sizeof(mach_msg_empty_rcv_t));
+
+ // mach_msg_server() and mach_msg_server_once() would consider whether
+ // |options| contains MACH_SEND_TRAILER and include MAX_TRAILER_SIZE in this
+ // computation if it does, but that option is ineffective on OS X.
+ const mach_msg_size_t reply_size = interface->MachMessageServerReplySize();
+ DCHECK_GE(reply_size, sizeof(mach_msg_empty_send_t));
+ const mach_msg_size_t reply_alloc = round_page(reply_size);
+
+ MachMessageBuffer request;
+ MachMessageBuffer reply;
+ bool received_any_request = false;
+ bool retry;
+
+ kern_return_t kr;
+
+ do {
+ retry = false;
+
+ kr = MachMessageAllocateReceive(&request,
+ options,
+ request_size,
+ receive_port,
+ deadline,
+ MACH_PORT_NULL,
+ !received_any_request);
+ if (kr == MACH_RCV_TOO_LARGE) {
+ switch (receive_large) {
+ case kReceiveLargeError:
+ break;
+
+ case kReceiveLargeIgnore:
+ // Try again, even in one-shot mode. The caller is expecting this
+ // method to take action on the first message in the queue, and has
+ // indicated that they want large messages to be ignored. The
+ // alternatives, which might involve returning MACH_MSG_SUCCESS,
+ // MACH_RCV_TIMED_OUT, or MACH_RCV_TOO_LARGE to a caller that
+ // specified one-shot behavior, all seem less correct than retrying.
+ MACH_LOG(WARNING, kr) << "mach_msg: ignoring large message";
+ retry = true;
+ continue;
+
+ case kReceiveLargeResize: {
+ mach_msg_size_t this_request_size = round_page(
+ round_msg(request.Header()->msgh_size) + trailer_alloc);
+ DCHECK_GT(this_request_size, request_size);
+
+ kr = MachMessageAllocateReceive(&request,
+ options & ~MACH_RCV_LARGE,
+ this_request_size,
+ receive_port,
+ deadline,
+ MACH_PORT_NULL,
+ !received_any_request);
+
+ break;
+ }
+ }
+ }
+
+ if (kr != MACH_MSG_SUCCESS) {
+ return kr;
+ }
+
+ received_any_request = true;
+
+ kr = reply.Reallocate(reply_alloc);
+ if (kr != KERN_SUCCESS) {
+ return kr;
+ }
+
+ mach_msg_header_t* request_header = request.Header();
+ mach_msg_header_t* reply_header = reply.Header();
+ bool destroy_complex_request = false;
+ interface->MachMessageServerFunction(
+ request_header, reply_header, &destroy_complex_request);
+
+ if (!(reply_header->msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
+ // This only works if the reply message is not complex, because otherwise,
+ // the location of the RetCode field is not known. It should be possible
+ // to locate the RetCode field by looking beyond the descriptors in a
+ // complex reply message, but this is not currently done. This behavior
+ // has not proven itself necessary in practice, and it’s not done by
+ // mach_msg_server() or mach_msg_server_once() either.
+ mig_reply_error_t* reply_mig =
+ reinterpret_cast<mig_reply_error_t*>(reply_header);
+ if (reply_mig->RetCode == MIG_NO_REPLY) {
+ reply_header->msgh_remote_port = MACH_PORT_NULL;
+ } else if (reply_mig->RetCode != KERN_SUCCESS &&
+ request_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) {
+ destroy_complex_request = true;
+ }
+ }
+
+ if (destroy_complex_request &&
+ request_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) {
+ request_header->msgh_remote_port = MACH_PORT_NULL;
+ mach_msg_destroy(request_header);
+ }
+
+ if (reply_header->msgh_remote_port != MACH_PORT_NULL) {
+ // Avoid blocking indefinitely. This duplicates the logic in 10.9.5
+ // xnu-2422.115.4/libsyscall/mach/mach_msg.c mach_msg_server_once(),
+ // although the special provision for sending to a send-once right is not
+ // made, because kernel keeps sends to a send-once right on the fast path
+ // without considering the user-specified timeout. See 10.9.5
+ // xnu-2422.115.4/osfmk/ipc/ipc_mqueue.c ipc_mqueue_send().
+ const MachMessageDeadline send_deadline =
+ deadline == kMachMessageDeadlineWaitIndefinitely
+ ? kMachMessageDeadlineNonblocking
+ : deadline;
+
+ kr = MachMessageWithDeadline(reply_header,
+ options | MACH_SEND_MSG,
+ 0,
+ MACH_PORT_NULL,
+ send_deadline,
+ MACH_PORT_NULL,
+ true);
+
+ if (kr != MACH_MSG_SUCCESS) {
+ if (kr == MACH_SEND_INVALID_DEST ||
+ kr == MACH_SEND_TIMED_OUT ||
+ kr == MACH_SEND_INTERRUPTED) {
+ mach_msg_destroy(reply_header);
+ }
+ return kr;
+ }
+ }
+ } while (persistent == kPersistent || retry);
+
+ return kr;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/mach_message_server.h b/chromium/third_party/crashpad/crashpad/util/mach/mach_message_server.h
new file mode 100644
index 00000000000..65de85c0cdc
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/mach_message_server.h
@@ -0,0 +1,179 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MACH_MACH_MESSAGE_SERVER_H_
+#define CRASHPAD_UTIL_MACH_MACH_MESSAGE_SERVER_H_
+
+#include <mach/mach.h>
+
+#include <set>
+
+#include "base/basictypes.h"
+
+namespace crashpad {
+
+//! \brief Runs a Mach message server to handle a Mach RPC request for MIG
+//! servers.
+//!
+//! The principal entry point to this interface is the static Run() method.
+class MachMessageServer {
+ public:
+ //! \brief A Mach RPC callback interface, called by Run().
+ class Interface {
+ public:
+ //! \brief Handles a Mach RPC request.
+ //!
+ //! This method is a stand-in for a MIG-generated Mach RPC server “demux”
+ //! function such as `exc_server()` and `mach_exc_server()`. Implementations
+ //! may call such a function directly. This method is expected to behave
+ //! exactly as these functions behave.
+ //!
+ //! \param[in] in The request message, received as a Mach message. Note that
+ //! this interface uses a `const` parameter for this purpose, whereas
+ //! MIG-generated “demux” functions do not.
+ //! \param[out] out The reply message. The caller allocates storage, and the
+ //! callee is expected to populate the reply message appropriately.
+ //! After returning, the caller will send this reply as a Mach message
+ //! via the message’s reply port.
+ //! \param[out] destroy_complex_request `true` if a complex request message
+ //! is to be destroyed even when handled successfully, `false`
+ //! otherwise. The traditional behavior is `false`. In this case, the
+ //! caller only destroys the request message in \a in when the reply
+ //! message in \a out is not complex and when it indicates a return code
+ //! other than `KERN_SUCCESS` or `MIG_NO_REPLY`. The assumption is that
+ //! the rights or out-of-line data carried in a complex message may be
+ //! retained by the server in this situation, and that it is the
+ //! responsibility of the server to release these resources as needed.
+ //! However, in many cases, these resources are not needed beyond the
+ //! duration of a request-reply transaction, and in such cases, it is
+ //! less error-prone to always have the caller,
+ //! MachMessageServer::Run(), destroy complex request messages. To
+ //! choose this behavior, this parameter should be set to `true`.
+ //!
+ //! \return `true` on success and `false` on failure, although the caller
+ //! ignores the return value. However, the return code to be included in
+ //! the reply message should be set as `mig_reply_error_t::RetCode`. The
+ //! non-`void` return value is used for increased compatibility with
+ //! MIG-generated functions.
+ virtual bool MachMessageServerFunction(const mach_msg_header_t* in,
+ mach_msg_header_t* out,
+ bool* destroy_complex_request) = 0;
+
+ //! \return The set of request message Mach message IDs that
+ //! MachMessageServerFunction() is able to handle.
+ virtual std::set<mach_msg_id_t> MachMessageServerRequestIDs() = 0;
+
+ //! \return The expected or maximum size, in bytes, of a request message to
+ //! be received as the \a in parameter of MachMessageServerFunction().
+ virtual mach_msg_size_t MachMessageServerRequestSize() = 0;
+
+ //! \return The maximum size, in bytes, of a reply message to be sent via
+ //! the \a out parameter of MachMessageServerFunction(). This value does
+ //! not need to include the size of any trailer to be sent with the
+ //! message.
+ virtual mach_msg_size_t MachMessageServerReplySize() = 0;
+
+ protected:
+ ~Interface() {}
+ };
+
+ //! \brief Informs Run() whether to handle a single request-reply transaction
+ //! or to run in a loop.
+ enum Persistent {
+ //! \brief Handle a single request-reply transaction and then return.
+ kOneShot = false,
+
+ //! \brief Run in a loop, potentially handling multiple request-reply
+ //! transactions.
+ kPersistent,
+ };
+
+ //! \brief Determines how to handle the reception of messages larger than the
+ //! size of the buffer allocated to store them.
+ enum ReceiveLarge {
+ //! \brief Return `MACH_RCV_TOO_LARGE` upon receipt of a large message.
+ //!
+ //! This mimics the default behavior of `mach_msg_server()` when `options`
+ //! does not contain `MACH_RCV_LARGE`.
+ kReceiveLargeError = 0,
+
+ //! \brief Ignore large messages, and attempt to receive the next queued
+ //! message upon encountering one.
+ //!
+ //! When a large message is encountered, a warning will be logged.
+ //!
+ //! `mach_msg()` will be called to receive the next message after a large
+ //! one even when accompanied by a #Persistent value of #kOneShot.
+ kReceiveLargeIgnore,
+
+ //! \brief Allocate an appropriately-sized buffer upon encountering a large
+ //! message. The buffer will be used to receive the message. This
+ //!
+ //! This mimics the behavior of `mach_msg_server()` when `options` contains
+ //! `MACH_RCV_LARGE`.
+ kReceiveLargeResize,
+ };
+
+ //! \brief Runs a Mach message server to handle a Mach RPC request for MIG
+ //! servers.
+ //!
+ //! This function listens for a request message and passes it to a callback
+ //! interface. A reponse is collected from that interface, and is sent back as
+ //! a reply.
+ //!
+ //! This function is similar to `mach_msg_server()` and
+ //! `mach_msg_server_once()`.
+ //!
+ //! \param[in] interface The MachMessageServerInterface that is responsible
+ //! for handling the message. Interface::MachMessageServerRequestSize() is
+ //! used as the receive size for the request message, and
+ //! Interface::MachMessageServerReplySize() is used as the
+ //! maximum size of the reply message. If \a options contains
+ //! `MACH_RCV_LARGE`, this function will retry a receive operation that
+ //! returns `MACH_RCV_TOO_LARGE` with an appropriately-sized buffer.
+ //! MachMessageServerInterface::MachMessageServerFunction() is called to
+ //! handle the request and populate the reply.
+ //! \param[in] receive_port The port on which to receive the request message.
+ //! \param[in] options Options suitable for mach_msg. For the defaults, use
+ //! `MACH_MSG_OPTION_NONE`. `MACH_RCV_LARGE` when specified here is
+ //! ignored. Set \a receive_large to #kReceiveLargeResize instead.
+ //! \param[in] persistent Chooses between one-shot and persistent operation.
+ //! \param[in] receive_large Determines the behavior upon encountering a
+ //! message larger than the receive buffer’s size.
+ //! \param[in] timeout_ms The maximum duration that this entire function will
+ //! run, in milliseconds. This may be #kMachMessageTimeoutNonblocking or
+ //! #kMachMessageTimeoutWaitIndefinitely. When \a persistent is
+ //! #kPersistent, the timeout applies to the overall duration of this
+ //! function, not to any individual `mach_msg()` call.
+ //!
+ //! \return On success, `KERN_SUCCESS` (when \a persistent is #kOneShot) or
+ //! `MACH_RCV_TIMED_OUT` (when \a persistent is #kOneShot and \a
+ //! timeout_ms is not #kMachMessageTimeoutWaitIndefinitely). This function
+ //! has no successful return value when \a persistent is #kPersistent and
+ //! \a timeout_ms is #kMachMessageTimeoutWaitIndefinitely. On failure,
+ //! returns a value identifying the nature of the error.
+ static mach_msg_return_t Run(Interface* interface,
+ mach_port_t receive_port,
+ mach_msg_options_t options,
+ Persistent persistent,
+ ReceiveLarge receive_large,
+ mach_msg_timeout_t timeout_ms);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(MachMessageServer);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MACH_MACH_MESSAGE_SERVER_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/mach_message_server_test.cc b/chromium/third_party/crashpad/crashpad/util/mach/mach_message_server_test.cc
new file mode 100644
index 00000000000..952fa4bfadc
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/mach_message_server_test.cc
@@ -0,0 +1,853 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/mach_message_server.h"
+
+#include <mach/mach.h>
+#include <string.h>
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/mac/scoped_mach_port.h"
+#include "gtest/gtest.h"
+#include "test/mac/mach_errors.h"
+#include "test/mac/mach_multiprocess.h"
+#include "util/file/file_io.h"
+#include "util/mach/mach_extensions.h"
+#include "util/mach/mach_message.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+class TestMachMessageServer : public MachMessageServer::Interface,
+ public MachMultiprocess {
+ public:
+ struct Options {
+ // The type of reply port that the client should put in its request message.
+ enum ReplyPortType {
+ // The normal reply port is the client’s local port, to which it holds
+ // a receive right. This allows the server to respond directly to the
+ // client. The client will expect a reply.
+ kReplyPortNormal,
+
+ // Use MACH_PORT_NULL as the reply port, which the server should detect
+ // avoid attempting to send a message to, and return success. The client
+ // will not expect a reply.
+ kReplyPortNull,
+
+ // Make the server see the reply port as a dead name by setting the reply
+ // port to a receive right and then destroying that right before the
+ // server processes the request. The server should return
+ // MACH_SEND_INVALID_DEST, and the client will not expect a reply.
+ kReplyPortDead,
+ };
+
+ Options()
+ : expect_server_interface_method_called(true),
+ parent_wait_for_child_pipe(false),
+ server_options(MACH_MSG_OPTION_NONE),
+ server_persistent(MachMessageServer::kOneShot),
+ server_receive_large(MachMessageServer::kReceiveLargeError),
+ server_timeout_ms(kMachMessageTimeoutWaitIndefinitely),
+ server_mig_retcode(KERN_SUCCESS),
+ server_destroy_complex(true),
+ expect_server_destroyed_complex(true),
+ expect_server_result(KERN_SUCCESS),
+ expect_server_transaction_count(1),
+ child_wait_for_parent_pipe_early(false),
+ client_send_request_count(1),
+ client_send_complex(false),
+ client_send_large(false),
+ client_reply_port_type(kReplyPortNormal),
+ client_expect_reply(true),
+ child_send_all_requests_before_receiving_any_replies(false),
+ child_wait_for_parent_pipe_late(false) {
+ }
+
+ // true if MachMessageServerFunction() is expected to be called.
+ bool expect_server_interface_method_called;
+
+ // true if the parent should wait for the child to write a byte to the pipe
+ // as a signal that the child is ready for the parent to begin its side of
+ // the test. This is used for nonblocking tests, which require that there
+ // be something in the server’s queue before attempting a nonblocking
+ // receive if the receive is to be successful.
+ bool parent_wait_for_child_pipe;
+
+ // Options to pass to MachMessageServer::Run() as the |options| parameter.
+ mach_msg_options_t server_options;
+
+ // Whether the server should run in one-shot or persistent mode.
+ MachMessageServer::Persistent server_persistent;
+
+ // The strategy for handling large messages.
+ MachMessageServer::ReceiveLarge server_receive_large;
+
+ // The server’s timeout in milliseconds, or kMachMessageTimeoutNonblocking
+ // or kMachMessageTimeoutWaitIndefinitely.
+ mach_msg_timeout_t server_timeout_ms;
+
+ // The return code that the server returns to the client via the
+ // mig_reply_error_t::RetCode field. A client would normally see this as
+ // a Mach RPC return value.
+ kern_return_t server_mig_retcode;
+
+ // The value that the server function should set its destroy_complex_request
+ // parameter to. This is true if resources sent in complex request messages
+ // should be destroyed, and false if they should not be destroyed, assuming
+ // that the server function indicates success.
+ bool server_destroy_complex;
+
+ // Whether to expect the server to destroy a complex message. Even if
+ // server_destroy_complex is false, a complex message will be destroyed if
+ // the MIG return code was unsuccessful.
+ bool expect_server_destroyed_complex;
+
+ // The expected return value from MachMessageServer::Run().
+ kern_return_t expect_server_result;
+
+ // The number of transactions that the server is expected to handle.
+ size_t expect_server_transaction_count;
+
+ // true if the child should wait for the parent to signal that it’s ready
+ // for the child to begin sending requests via the pipe. This is done if the
+ // parent needs to perform operations on its receive port before the child
+ // should be permitted to send anything to it. Currently, this is used to
+ // allow the parent to ensure that the receive port’s queue length is high
+ // enough before the child begins attempting to fill it.
+ bool child_wait_for_parent_pipe_early;
+
+ // The number of requests that the client should send to the server.
+ size_t client_send_request_count;
+
+ // true if the client should send a complex message, one that carries a port
+ // descriptor in its body. Normally false.
+ bool client_send_complex;
+
+ // true if the client should send a larger message than the server has
+ // allocated space to receive. The server’s response is directed by
+ // server_receive_large.
+ bool client_send_large;
+
+ // The type of reply port that the client should provide in its request’s
+ // mach_msg_header_t::msgh_local_port, which will appear to the server as
+ // mach_msg_header_t::msgh_remote_port.
+ ReplyPortType client_reply_port_type;
+
+ // true if the client should wait for a reply from the server. For
+ // non-normal reply ports or requests which the server responds to with no
+ // reply (MIG_NO_REPLY), the server will either not send a reply or not
+ // succeed in sending a reply, and the child process should not wait for
+ // one.
+ bool client_expect_reply;
+
+ // true if the client should send all requests before attempting to receive
+ // any replies from the server. This is used for the persistent nonblocking
+ // test, which requires the client to fill the server’s queue before the
+ // server can attempt processing it.
+ bool child_send_all_requests_before_receiving_any_replies;
+
+ // true if the child should wait to receive a byte from the parent before
+ // exiting. This can be used to keep a receive right in the child alive
+ // until the parent has a chance to verify that it’s holding a send right.
+ // Otherwise, the right might appear in the parent as a dead name if the
+ // child exited before the parent had a chance to examine it. This would be
+ // a race.
+ bool child_wait_for_parent_pipe_late;
+ };
+
+ explicit TestMachMessageServer(const Options& options)
+ : MachMessageServer::Interface(),
+ MachMultiprocess(),
+ options_(options),
+ child_complex_message_port_(),
+ parent_complex_message_port_(MACH_PORT_NULL) {
+ }
+
+ // Runs the test.
+ void Test() {
+ EXPECT_EQ(requests_, replies_);
+ uint32_t start = requests_;
+
+ Run();
+
+ EXPECT_EQ(requests_, replies_);
+ EXPECT_EQ(options_.expect_server_transaction_count, requests_ - start);
+ }
+
+ // MachMessageServerInterface:
+
+ virtual bool MachMessageServerFunction(
+ const mach_msg_header_t* in,
+ mach_msg_header_t* out,
+ bool* destroy_complex_request) override {
+ *destroy_complex_request = options_.server_destroy_complex;
+
+ EXPECT_TRUE(options_.expect_server_interface_method_called);
+ if (!options_.expect_server_interface_method_called) {
+ return false;
+ }
+
+ struct ReceiveRequestMessage : public RequestMessage {
+ mach_msg_trailer_t trailer;
+ };
+
+ struct ReceiveLargeRequestMessage : public LargeRequestMessage {
+ mach_msg_trailer_t trailer;
+ };
+
+ const ReceiveRequestMessage* request =
+ reinterpret_cast<const ReceiveRequestMessage*>(in);
+ const mach_msg_bits_t expect_msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, MACH_MSG_TYPE_MOVE_SEND) |
+ (options_.client_send_complex ? MACH_MSGH_BITS_COMPLEX : 0);
+ EXPECT_EQ(expect_msgh_bits, request->header.msgh_bits);
+ EXPECT_EQ(options_.client_send_large ? sizeof(LargeRequestMessage) :
+ sizeof(RequestMessage),
+ request->header.msgh_size);
+ if (options_.client_reply_port_type == Options::kReplyPortNormal) {
+ EXPECT_EQ(RemotePort(), request->header.msgh_remote_port);
+ }
+ EXPECT_EQ(LocalPort(), request->header.msgh_local_port);
+ EXPECT_EQ(kRequestMessageID, request->header.msgh_id);
+ if (options_.client_send_complex) {
+ EXPECT_EQ(1u, request->body.msgh_descriptor_count);
+ EXPECT_NE(kMachPortNull, request->port_descriptor.name);
+ parent_complex_message_port_ = request->port_descriptor.name;
+ EXPECT_EQ(implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_MOVE_SEND),
+ request->port_descriptor.disposition);
+ EXPECT_EQ(
+ implicit_cast<mach_msg_descriptor_type_t>(MACH_MSG_PORT_DESCRIPTOR),
+ request->port_descriptor.type);
+ } else {
+ EXPECT_EQ(0u, request->body.msgh_descriptor_count);
+ EXPECT_EQ(kMachPortNull, request->port_descriptor.name);
+ EXPECT_EQ(0u, request->port_descriptor.disposition);
+ EXPECT_EQ(0u, request->port_descriptor.type);
+ }
+ EXPECT_EQ(0, memcmp(&request->ndr, &NDR_record, sizeof(NDR_record)));
+ EXPECT_EQ(requests_, request->number);
+
+ // Look for the trailer in the right spot, depending on whether the request
+ // message was a RequestMessage or a LargeRequestMessage.
+ const mach_msg_trailer_t* trailer;
+ if (options_.client_send_large) {
+ const ReceiveLargeRequestMessage* large_request =
+ reinterpret_cast<const ReceiveLargeRequestMessage*>(request);
+ for (size_t index = 0; index < sizeof(large_request->data); ++index) {
+ EXPECT_EQ('!', large_request->data[index]);
+ }
+ trailer = &large_request->trailer;
+ } else {
+ trailer = &request->trailer;
+ }
+
+ EXPECT_EQ(implicit_cast<mach_msg_trailer_type_t>(MACH_MSG_TRAILER_FORMAT_0),
+ trailer->msgh_trailer_type);
+ EXPECT_EQ(MACH_MSG_TRAILER_MINIMUM_SIZE, trailer->msgh_trailer_size);
+
+ ++requests_;
+
+ ReplyMessage* reply = reinterpret_cast<ReplyMessage*>(out);
+ reply->Head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
+ reply->Head.msgh_size = sizeof(*reply);
+ reply->Head.msgh_remote_port = request->header.msgh_remote_port;
+ reply->Head.msgh_local_port = MACH_PORT_NULL;
+ reply->Head.msgh_id = kReplyMessageID;
+ reply->NDR = NDR_record;
+ reply->RetCode = options_.server_mig_retcode;
+ reply->number = replies_++;
+
+ return true;
+ }
+
+ std::set<mach_msg_id_t> MachMessageServerRequestIDs() override {
+ const mach_msg_id_t request_ids[] = {kRequestMessageID};
+ return std::set<mach_msg_id_t>(
+ &request_ids[0], &request_ids[arraysize(request_ids)]);
+ }
+
+ mach_msg_size_t MachMessageServerRequestSize() override {
+ return sizeof(RequestMessage);
+ }
+
+ mach_msg_size_t MachMessageServerReplySize() override {
+ return sizeof(ReplyMessage);
+ }
+
+ private:
+ struct RequestMessage : public mach_msg_base_t {
+ // If body.msgh_descriptor_count is 0, port_descriptor will still be
+ // present, but it will be zeroed out. It wouldn’t normally be present in a
+ // message froma MIG-generated interface, but it’s harmless and simpler to
+ // leave it here and just treat it as more data.
+ mach_msg_port_descriptor_t port_descriptor;
+
+ NDR_record_t ndr;
+ uint32_t number;
+ };
+
+ // LargeRequestMessage is larger enough than a regular RequestMessage to
+ // ensure that whatever buffer was allocated to receive a RequestMessage is
+ // not large enough to receive a LargeRequestMessage.
+ struct LargeRequestMessage : public RequestMessage {
+ uint8_t data[4 * PAGE_SIZE];
+ };
+
+ struct ReplyMessage : public mig_reply_error_t {
+ uint32_t number;
+ };
+
+ // MachMultiprocess:
+
+ void MachMultiprocessParent() override {
+ mach_port_t local_port = LocalPort();
+
+ kern_return_t kr;
+ if (options_.child_send_all_requests_before_receiving_any_replies) {
+ // On Mac OS X 10.10, the queue limit of a new Mach port seems to be 2
+ // by default, which is below the value of MACH_PORT_QLIMIT_DEFAULT. Set
+ // the port’s queue limit explicitly here.
+ mach_port_limits limits = {};
+ limits.mpl_qlimit = MACH_PORT_QLIMIT_DEFAULT;
+ kr = mach_port_set_attributes(mach_task_self(),
+ local_port,
+ MACH_PORT_LIMITS_INFO,
+ reinterpret_cast<mach_port_info_t>(&limits),
+ MACH_PORT_LIMITS_INFO_COUNT);
+ ASSERT_EQ(KERN_SUCCESS, kr)
+ << MachErrorMessage(kr, "mach_port_set_attributes");
+ }
+
+ if (options_.child_wait_for_parent_pipe_early) {
+ // Tell the child to begin sending messages.
+ char c = '\0';
+ CheckedWriteFile(WritePipeHandle(), &c, 1);
+ }
+
+ if (options_.parent_wait_for_child_pipe) {
+ // Wait until the child is done sending what it’s going to send.
+ char c;
+ CheckedReadFile(ReadPipeHandle(), &c, 1);
+ EXPECT_EQ('\0', c);
+ }
+
+ ASSERT_EQ(options_.expect_server_result,
+ (kr = MachMessageServer::Run(this,
+ local_port,
+ options_.server_options,
+ options_.server_persistent,
+ options_.server_receive_large,
+ options_.server_timeout_ms)))
+ << MachErrorMessage(kr, "MachMessageServer");
+
+ if (options_.client_send_complex) {
+ EXPECT_NE(kMachPortNull, parent_complex_message_port_);
+ mach_port_type_t type;
+
+ if (!options_.expect_server_destroyed_complex) {
+ // MachMessageServer should not have destroyed the resources sent in the
+ // complex request message.
+ kr = mach_port_type(
+ mach_task_self(), parent_complex_message_port_, &type);
+ EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_type");
+ EXPECT_EQ(MACH_PORT_TYPE_SEND, type);
+
+ // Destroy the resources here.
+ kr = mach_port_deallocate(
+ mach_task_self(), parent_complex_message_port_);
+ EXPECT_EQ(KERN_SUCCESS, kr)
+ << MachErrorMessage(kr, "mach_port_deallocate");
+ }
+
+ // The kernel won’t have reused the same name for another Mach port in
+ // this task so soon. It’s possible that something else in this task could
+ // have reused the name, but it’s unlikely for that to have happened in
+ // this test environment.
+ kr = mach_port_type(
+ mach_task_self(), parent_complex_message_port_, &type);
+ EXPECT_EQ(KERN_INVALID_NAME, kr)
+ << MachErrorMessage(kr, "mach_port_type");
+ }
+
+ if (options_.child_wait_for_parent_pipe_late) {
+ // Let the child know it’s safe to exit.
+ char c = '\0';
+ CheckedWriteFile(WritePipeHandle(), &c, 1);
+ }
+ }
+
+ void MachMultiprocessChild() override {
+ if (options_.child_wait_for_parent_pipe_early) {
+ // Wait until the parent is done setting things up on its end.
+ char c;
+ CheckedReadFile(ReadPipeHandle(), &c, 1);
+ EXPECT_EQ('\0', c);
+ }
+
+ for (size_t index = 0;
+ index < options_.client_send_request_count;
+ ++index) {
+ if (options_.child_send_all_requests_before_receiving_any_replies) {
+ // For this test, all of the messages need to go into the queue before
+ // the parent is allowed to start processing them. Don’t attempt to
+ // process replies before all of the requests are sent, because the
+ // server won’t have sent any replies until all of the requests are in
+ // its queue.
+ ASSERT_NO_FATAL_FAILURE(ChildSendRequest());
+ } else {
+ ASSERT_NO_FATAL_FAILURE(ChildSendRequestAndWaitForReply());
+ }
+ }
+
+ if (options_.parent_wait_for_child_pipe &&
+ options_.child_send_all_requests_before_receiving_any_replies) {
+ // Now that all of the requests have been sent, let the parent know that
+ // it’s safe to begin processing them, and then wait for the replies.
+ ASSERT_NO_FATAL_FAILURE(ChildNotifyParentViaPipe());
+
+ for (size_t index = 0;
+ index < options_.client_send_request_count;
+ ++index) {
+ ASSERT_NO_FATAL_FAILURE(ChildWaitForReply());
+ }
+ }
+
+ if (options_.child_wait_for_parent_pipe_late) {
+ char c;
+ CheckedReadFile(ReadPipeHandle(), &c, 1);
+ ASSERT_EQ('\0', c);
+ }
+ }
+
+ // In the child process, sends a request message to the server.
+ void ChildSendRequest() {
+ // local_receive_port_owner will the receive right that is created in this
+ // scope and intended to be destroyed when leaving this scope, after it has
+ // been carried in a Mach message.
+ base::mac::ScopedMachReceiveRight local_receive_port_owner;
+
+ // A LargeRequestMessage is always allocated, but the message that will be
+ // sent will be a normal RequestMessage due to the msgh_size field
+ // indicating the size of the smaller base structure unless
+ // options_.client_send_large is true.
+ LargeRequestMessage request = {};
+
+ request.header.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND) |
+ (options_.client_send_complex ? MACH_MSGH_BITS_COMPLEX : 0);
+ request.header.msgh_size = options_.client_send_large ?
+ sizeof(LargeRequestMessage) : sizeof(RequestMessage);
+ request.header.msgh_remote_port = RemotePort();
+ kern_return_t kr;
+ switch (options_.client_reply_port_type) {
+ case Options::kReplyPortNormal:
+ request.header.msgh_local_port = LocalPort();
+ break;
+ case Options::kReplyPortNull:
+ request.header.msgh_local_port = MACH_PORT_NULL;
+ break;
+ case Options::kReplyPortDead: {
+ // Use a newly-allocated receive right that will be destroyed when this
+ // method returns. A send right will be made from this receive right and
+ // carried in the request message to the server. By the time the server
+ // looks at the right, it will have become a dead name.
+ local_receive_port_owner.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
+ ASSERT_NE(kMachPortNull, local_receive_port_owner);
+ request.header.msgh_local_port = local_receive_port_owner;
+ break;
+ }
+ }
+ request.header.msgh_id = kRequestMessageID;
+ if (options_.client_send_complex) {
+ // Allocate a new receive right in this process and make a send right that
+ // will appear in the parent process. This is used to test that the server
+ // properly handles ownership of resources received in complex messages.
+ request.body.msgh_descriptor_count = 1;
+ child_complex_message_port_.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
+ ASSERT_NE(kMachPortNull, child_complex_message_port_);
+ request.port_descriptor.name = child_complex_message_port_;
+ request.port_descriptor.disposition = MACH_MSG_TYPE_MAKE_SEND;
+ request.port_descriptor.type = MACH_MSG_PORT_DESCRIPTOR;
+ } else {
+ request.body.msgh_descriptor_count = 0;
+ request.port_descriptor.name = MACH_PORT_NULL;
+ request.port_descriptor.disposition = 0;
+ request.port_descriptor.type = 0;
+ }
+ request.ndr = NDR_record;
+ request.number = requests_++;
+
+ if (options_.client_send_large) {
+ memset(request.data, '!', sizeof(request.data));
+ }
+
+ kr = mach_msg(&request.header,
+ MACH_SEND_MSG | MACH_SEND_TIMEOUT,
+ request.header.msgh_size,
+ 0,
+ MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+ ASSERT_EQ(MACH_MSG_SUCCESS, kr) << MachErrorMessage(kr, "mach_msg");
+ }
+
+ // In the child process, waits for a reply message from the server.
+ void ChildWaitForReply() {
+ if (!options_.client_expect_reply) {
+ // The client shouldn’t expect a reply when it didn’t send a good reply
+ // port with its request, or when testing the server behaving in a way
+ // that doesn’t send replies.
+ return;
+ }
+
+ struct ReceiveReplyMessage : public ReplyMessage {
+ mach_msg_trailer_t trailer;
+ };
+
+ ReceiveReplyMessage reply = {};
+ kern_return_t kr = mach_msg(&reply.Head,
+ MACH_RCV_MSG,
+ 0,
+ sizeof(reply),
+ LocalPort(),
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+ ASSERT_EQ(MACH_MSG_SUCCESS, kr) << MachErrorMessage(kr, "mach_msg");
+
+ ASSERT_EQ(implicit_cast<mach_msg_bits_t>(
+ MACH_MSGH_BITS(0, MACH_MSG_TYPE_MOVE_SEND)), reply.Head.msgh_bits);
+ ASSERT_EQ(sizeof(ReplyMessage), reply.Head.msgh_size);
+ ASSERT_EQ(kMachPortNull, reply.Head.msgh_remote_port);
+ ASSERT_EQ(LocalPort(), reply.Head.msgh_local_port);
+ ASSERT_EQ(kReplyMessageID, reply.Head.msgh_id);
+ ASSERT_EQ(0, memcmp(&reply.NDR, &NDR_record, sizeof(NDR_record)));
+ ASSERT_EQ(options_.server_mig_retcode, reply.RetCode);
+ ASSERT_EQ(replies_, reply.number);
+ ASSERT_EQ(implicit_cast<mach_msg_trailer_type_t>(MACH_MSG_TRAILER_FORMAT_0),
+ reply.trailer.msgh_trailer_type);
+ ASSERT_EQ(MACH_MSG_TRAILER_MINIMUM_SIZE, reply.trailer.msgh_trailer_size);
+
+ ++replies_;
+ }
+
+ // For test types where the child needs to notify the server in the parent
+ // that the child is ready, this method will send a byte via the POSIX pipe.
+ // The parent will be waiting in a read() on this pipe, and will proceed to
+ // running MachMessageServer() once it’s received.
+ void ChildNotifyParentViaPipe() {
+ char c = '\0';
+ CheckedWriteFile(WritePipeHandle(), &c, 1);
+ }
+
+ // In the child process, sends a request message to the server and then
+ // receives a reply message.
+ void ChildSendRequestAndWaitForReply() {
+ ASSERT_NO_FATAL_FAILURE(ChildSendRequest());
+
+ if (options_.parent_wait_for_child_pipe &&
+ !options_.child_send_all_requests_before_receiving_any_replies) {
+ // The parent is waiting to read a byte to indicate that the message has
+ // been placed in the queue.
+ ASSERT_NO_FATAL_FAILURE(ChildNotifyParentViaPipe());
+ }
+
+ ASSERT_NO_FATAL_FAILURE(ChildWaitForReply());
+ }
+
+ const Options& options_;
+
+ // A receive right allocated in the child process. A send right will be
+ // created from this right and sent to the parent parent process in the
+ // request message.
+ base::mac::ScopedMachReceiveRight child_complex_message_port_;
+
+ // The send right received in the parent process. This right is stored in a
+ // member variable to test that resources carried in complex messages are
+ // properly destroyed in the server when expected.
+ mach_port_t parent_complex_message_port_;
+
+ static uint32_t requests_;
+ static uint32_t replies_;
+
+ static const mach_msg_id_t kRequestMessageID = 16237;
+ static const mach_msg_id_t kReplyMessageID = kRequestMessageID + 100;
+
+ DISALLOW_COPY_AND_ASSIGN(TestMachMessageServer);
+};
+
+uint32_t TestMachMessageServer::requests_;
+uint32_t TestMachMessageServer::replies_;
+const mach_msg_id_t TestMachMessageServer::kRequestMessageID;
+const mach_msg_id_t TestMachMessageServer::kReplyMessageID;
+
+TEST(MachMessageServer, Basic) {
+ // The client sends one message to the server, which will wait indefinitely in
+ // blocking mode for it.
+ TestMachMessageServer::Options options;
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+TEST(MachMessageServer, NonblockingNoMessage) {
+ // The server waits in nonblocking mode and the client sends nothing, so the
+ // server should return immediately without processing any message.
+ TestMachMessageServer::Options options;
+ options.expect_server_interface_method_called = false;
+ options.server_timeout_ms = kMachMessageTimeoutNonblocking;
+ options.expect_server_result = MACH_RCV_TIMED_OUT;
+ options.expect_server_transaction_count = 0;
+ options.client_send_request_count = 0;
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+TEST(MachMessageServer, TimeoutNoMessage) {
+ // The server waits in blocking mode for one message, but with a timeout. The
+ // client sends no message, so the server returns after the timeout.
+ TestMachMessageServer::Options options;
+ options.expect_server_interface_method_called = false;
+ options.server_timeout_ms = 10;
+ options.expect_server_result = MACH_RCV_TIMED_OUT;
+ options.expect_server_transaction_count = 0;
+ options.client_send_request_count = 0;
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+TEST(MachMessageServer, Nonblocking) {
+ // The client sends one message to the server and then signals the server that
+ // it’s safe to start waiting for it in nonblocking mode. The message is in
+ // the server’s queue, so it’s able to receive it when it begins listening in
+ // nonblocking mode.
+ TestMachMessageServer::Options options;
+ options.parent_wait_for_child_pipe = true;
+ options.server_timeout_ms = kMachMessageTimeoutNonblocking;
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+TEST(MachMessageServer, Timeout) {
+ // The client sends one message to the server, which will wait in blocking
+ // mode for it up to a specific timeout.
+ TestMachMessageServer::Options options;
+ options.server_timeout_ms = 10;
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+TEST(MachMessageServer, PersistentTenMessages) {
+ // The server waits for as many messages as it can receive in blocking mode
+ // with a timeout. The client sends several messages, and the server processes
+ // them all.
+ TestMachMessageServer::Options options;
+ options.server_persistent = MachMessageServer::kPersistent;
+ options.server_timeout_ms = 10;
+ options.expect_server_result = MACH_RCV_TIMED_OUT;
+ options.expect_server_transaction_count = 10;
+ options.client_send_request_count = 10;
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+TEST(MachMessageServer, PersistentNonblockingFourMessages) {
+ // The client sends several messages to the server and then signals the server
+ // that it’s safe to start waiting for them in nonblocking mode. The server
+ // then listens for them in nonblocking persistent mode, and receives all of
+ // them because they’ve been queued up. The client doesn’t wait for the
+ // replies until after it’s put all of its requests into the server’s queue.
+ //
+ // This test is sensitive to the length of the IPC queue limit. Mach ports
+ // normally have a queue length limit of MACH_PORT_QLIMIT_DEFAULT (which is
+ // MACH_PORT_QLIMIT_BASIC, or 5). The number of messages sent for this test
+ // must be below this, because the server does not begin dequeueing request
+ // messages until the client has finished sending them.
+ //
+ // The queue limit on new ports has been seen to be below
+ // MACH_PORT_QLIMIT_DEFAULT, so it will explicitly be set by
+ // mach_port_set_attributes() for this test. This needs to happen before the
+ // child is allowed to begin sending messages, so
+ // child_wait_for_parent_pipe_early is used to make the child wait until the
+ // parent is ready.
+ const size_t kTransactionCount = 4;
+ static_assert(kTransactionCount <= MACH_PORT_QLIMIT_DEFAULT,
+ "must not exceed queue limit");
+
+ TestMachMessageServer::Options options;
+ options.parent_wait_for_child_pipe = true;
+ options.server_persistent = MachMessageServer::kPersistent;
+ options.server_timeout_ms = kMachMessageTimeoutNonblocking;
+ options.expect_server_result = MACH_RCV_TIMED_OUT;
+ options.expect_server_transaction_count = kTransactionCount;
+ options.child_wait_for_parent_pipe_early = true;
+ options.client_send_request_count = kTransactionCount;
+ options.child_send_all_requests_before_receiving_any_replies = true;
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+TEST(MachMessageServer, ReturnCodeInvalidArgument) {
+ // This tests that the mig_reply_error_t::RetCode field is properly returned
+ // to the client.
+ TestMachMessageServer::Options options;
+ options.server_mig_retcode = KERN_INVALID_ARGUMENT;
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+TEST(MachMessageServer, ReturnCodeNoReply) {
+ // This tests that when mig_reply_error_t::RetCode is set to MIG_NO_REPLY, no
+ // response is sent to the client.
+ TestMachMessageServer::Options options;
+ options.server_mig_retcode = MIG_NO_REPLY;
+ options.client_expect_reply = false;
+ options.child_wait_for_parent_pipe_late = true;
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+TEST(MachMessageServer, ReplyPortNull) {
+ // The client sets its reply port to MACH_PORT_NULL. The server should see
+ // this and avoid sending a message to the null port. No reply message is
+ // sent and the server returns success.
+ TestMachMessageServer::Options options;
+ options.client_reply_port_type =
+ TestMachMessageServer::Options::kReplyPortNull;
+ options.client_expect_reply = false;
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+TEST(MachMessageServer, ReplyPortDead) {
+ // The client allocates a new port and uses it as the reply port in its
+ // request message, and then deallocates its receive right to that port. It
+ // then signals the server to process the request message. The server’s view
+ // of the port is that it is a dead name. The server function will return
+ // MACH_SEND_INVALID_DEST because it’s not possible to send a message to a
+ // dead name.
+ TestMachMessageServer::Options options;
+ options.parent_wait_for_child_pipe = true;
+ options.expect_server_result = MACH_SEND_INVALID_DEST;
+ options.client_reply_port_type =
+ TestMachMessageServer::Options::kReplyPortDead;
+ options.client_expect_reply = false;
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+TEST(MachMessageServer, Complex) {
+ // The client allocates a new receive right and sends a complex request
+ // message to the server with a send right made out of this receive right. The
+ // server receives this message and is instructed to destroy the send right
+ // when it is done handling the request-reply transaction. The former send
+ // right is verified to be invalid after the server runs. This test ensures
+ // that resources transferred to a server process temporarily aren’t leaked.
+ TestMachMessageServer::Options options;
+ options.client_send_complex = true;
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+TEST(MachMessageServer, ComplexNotDestroyed) {
+ // As in MachMessageServer.Complex, but the server is instructed not to
+ // destroy the send right. After the server runs, the send right is verified
+ // to continue to exist in the server task. The client process is then
+ // signalled by pipe that it’s safe to exit so that the send right in the
+ // server task doesn’t prematurely become a dead name. This test ensures that
+ // rights that are expected to be retained in the server task are properly
+ // retained.
+ TestMachMessageServer::Options options;
+ options.server_destroy_complex = false;
+ options.expect_server_destroyed_complex = false;
+ options.client_send_complex = true;
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+TEST(MachMessageServer, ComplexDestroyedInvalidArgument) {
+ // As in MachMessageServer.ComplexNotDestroyed, but the server does not return
+ // a successful code via MIG. The server is expected to destroy resources in
+ // this case, because server_destroy_complex = false is only honored when a
+ // MIG request is handled successfully or with no reply.
+ TestMachMessageServer::Options options;
+ options.server_mig_retcode = KERN_INVALID_TASK;
+ options.server_destroy_complex = false;
+ options.client_send_complex = true;
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+TEST(MachMessageServer, ComplexNotDestroyedNoReply) {
+ // As in MachMessageServer.ComplexNotDestroyed, but the server does not send
+ // a reply message and is expected to retain the send right in the server
+ // task.
+ TestMachMessageServer::Options options;
+ options.server_mig_retcode = MIG_NO_REPLY;
+ options.server_destroy_complex = false;
+ options.expect_server_destroyed_complex = false;
+ options.client_send_complex = true;
+ options.client_expect_reply = false;
+ options.child_wait_for_parent_pipe_late = true;
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+TEST(MachMessageServer, ReceiveLargeError) {
+ // The client sends a request to the server that is larger than the server is
+ // expecting. server_receive_large is kReceiveLargeError, so the request is
+ // destroyed and the server returns a MACH_RCV_TOO_LARGE error. The client
+ // does not receive a reply.
+ TestMachMessageServer::Options options;
+ options.expect_server_result = MACH_RCV_TOO_LARGE;
+ options.expect_server_transaction_count = 0;
+ options.client_send_large = true;
+ options.client_expect_reply = false;
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+TEST(MachMessageServer, ReceiveLargeRetry) {
+ // The client sends a request to the server that is larger than the server is
+ // initially expecting. server_receive_large is kReceiveLargeResize, so a new
+ // buffer is allocated to receive the message. The server receives the large
+ // request message, processes it, and returns a reply to the client.
+ TestMachMessageServer::Options options;
+ options.server_receive_large = MachMessageServer::kReceiveLargeResize;
+ options.client_send_large = true;
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+TEST(MachMessageServer, ReceiveLargeIgnore) {
+ // The client sends a request to the server that is larger than the server is
+ // expecting. server_receive_large is kReceiveLargeIgnore, so the request is
+ // destroyed but the server does not consider this an error. The server is
+ // running in blocking mode with a timeout, and continues to wait for a
+ // message until it times out. The client does not receive a reply.
+ TestMachMessageServer::Options options;
+ options.server_receive_large = MachMessageServer::kReceiveLargeIgnore;
+ options.server_timeout_ms = 10;
+ options.expect_server_result = MACH_RCV_TIMED_OUT;
+ options.expect_server_transaction_count = 0;
+ options.client_send_large = true;
+ options.client_expect_reply = false;
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/mach_message_test.cc b/chromium/third_party/crashpad/crashpad/util/mach/mach_message_test.cc
new file mode 100644
index 00000000000..6e91dccb5ab
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/mach_message_test.cc
@@ -0,0 +1,153 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/mach_message.h"
+
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/mac/scoped_mach_port.h"
+#include "gtest/gtest.h"
+#include "test/mac/mach_errors.h"
+#include "util/mach/mach_extensions.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(MachMessage, MachMessageDeadlineFromTimeout) {
+ MachMessageDeadline deadline_0 =
+ MachMessageDeadlineFromTimeout(kMachMessageTimeoutNonblocking);
+ EXPECT_EQ(kMachMessageDeadlineNonblocking, deadline_0);
+
+ deadline_0 =
+ MachMessageDeadlineFromTimeout(kMachMessageTimeoutWaitIndefinitely);
+ EXPECT_EQ(kMachMessageDeadlineWaitIndefinitely, deadline_0);
+
+ deadline_0 = MachMessageDeadlineFromTimeout(1);
+ MachMessageDeadline deadline_1 = MachMessageDeadlineFromTimeout(100);
+
+ EXPECT_NE(kMachMessageDeadlineNonblocking, deadline_0);
+ EXPECT_NE(kMachMessageDeadlineWaitIndefinitely, deadline_0);
+ EXPECT_NE(kMachMessageDeadlineNonblocking, deadline_1);
+ EXPECT_NE(kMachMessageDeadlineWaitIndefinitely, deadline_1);
+ EXPECT_GE(deadline_1, deadline_0);
+}
+
+TEST(MachMessage, PrepareMIGReplyFromRequest_SetMIGReplyError) {
+ mach_msg_header_t request;
+ request.msgh_bits =
+ MACH_MSGH_BITS_COMPLEX |
+ MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND);
+ request.msgh_size = 64;
+ request.msgh_remote_port = 0x01234567;
+ request.msgh_local_port = 0x89abcdef;
+ request.msgh_reserved = 0xa5a5a5a5;
+ request.msgh_id = 1011;
+
+ mig_reply_error_t reply;
+
+ // PrepareMIGReplyFromRequest() doesn’t touch this field.
+ reply.RetCode = MIG_TYPE_ERROR;
+
+ PrepareMIGReplyFromRequest(&request, &reply.Head);
+
+ EXPECT_EQ(implicit_cast<mach_msg_bits_t>(
+ MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0)),
+ reply.Head.msgh_bits);
+ EXPECT_EQ(sizeof(reply), reply.Head.msgh_size);
+ EXPECT_EQ(request.msgh_remote_port, reply.Head.msgh_remote_port);
+ EXPECT_EQ(kMachPortNull, reply.Head.msgh_local_port);
+ EXPECT_EQ(0u, reply.Head.msgh_reserved);
+ EXPECT_EQ(1111, reply.Head.msgh_id);
+ EXPECT_EQ(NDR_record.mig_vers, reply.NDR.mig_vers);
+ EXPECT_EQ(NDR_record.if_vers, reply.NDR.if_vers);
+ EXPECT_EQ(NDR_record.reserved1, reply.NDR.reserved1);
+ EXPECT_EQ(NDR_record.mig_encoding, reply.NDR.mig_encoding);
+ EXPECT_EQ(NDR_record.int_rep, reply.NDR.int_rep);
+ EXPECT_EQ(NDR_record.char_rep, reply.NDR.char_rep);
+ EXPECT_EQ(NDR_record.float_rep, reply.NDR.float_rep);
+ EXPECT_EQ(NDR_record.reserved2, reply.NDR.reserved2);
+ EXPECT_EQ(MIG_TYPE_ERROR, reply.RetCode);
+
+ SetMIGReplyError(&reply.Head, MIG_BAD_ID);
+
+ EXPECT_EQ(MIG_BAD_ID, reply.RetCode);
+}
+
+TEST(MachMessage, MachMessageTrailerFromHeader) {
+ mach_msg_empty_t empty;
+ empty.send.header.msgh_size = sizeof(mach_msg_empty_send_t);
+ EXPECT_EQ(&empty.rcv.trailer,
+ MachMessageTrailerFromHeader(&empty.rcv.header));
+
+ struct TestSendMessage : public mach_msg_header_t {
+ uint8_t data[126];
+ };
+ struct TestReceiveMessage : public TestSendMessage {
+ mach_msg_trailer_t trailer;
+ };
+ union TestMessage {
+ TestSendMessage send;
+ TestReceiveMessage receive;
+ };
+
+ TestMessage test;
+ test.send.msgh_size = sizeof(TestSendMessage);
+ EXPECT_EQ(&test.receive.trailer, MachMessageTrailerFromHeader(&test.receive));
+}
+
+TEST(MachMessage, AuditPIDFromMachMessageTrailer) {
+ base::mac::ScopedMachReceiveRight port(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
+ ASSERT_NE(kMachPortNull, port);
+
+ mach_msg_empty_send_t send = {};
+ send.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND_ONCE, 0);
+ send.header.msgh_size = sizeof(send);
+ send.header.msgh_remote_port = port;
+ mach_msg_return_t mr =
+ MachMessageWithDeadline(&send.header,
+ MACH_SEND_MSG,
+ 0,
+ MACH_PORT_NULL,
+ kMachMessageDeadlineNonblocking,
+ MACH_PORT_NULL,
+ false);
+ ASSERT_EQ(MACH_MSG_SUCCESS, mr)
+ << MachErrorMessage(mr, "MachMessageWithDeadline send");
+
+ struct EmptyReceiveMessageWithAuditTrailer : public mach_msg_empty_send_t {
+ union {
+ mach_msg_trailer_t trailer;
+ mach_msg_audit_trailer_t audit_trailer;
+ };
+ };
+
+ EmptyReceiveMessageWithAuditTrailer receive;
+ mr = MachMessageWithDeadline(&receive.header,
+ MACH_RCV_MSG | kMachMessageReceiveAuditTrailer,
+ sizeof(receive),
+ port,
+ kMachMessageDeadlineNonblocking,
+ MACH_PORT_NULL,
+ false);
+ ASSERT_EQ(MACH_MSG_SUCCESS, mr)
+ << MachErrorMessage(mr, "MachMessageWithDeadline receive");
+
+ EXPECT_EQ(getpid(), AuditPIDFromMachMessageTrailer(&receive.trailer));
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/mig.py b/chromium/third_party/crashpad/crashpad/util/mach/mig.py
new file mode 100755
index 00000000000..2d248854d59
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/mig.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import re
+import subprocess
+import sys
+
+def FixUserImplementation(implementation):
+ """Rewrites a MIG-generated user implementation (.c) file.
+
+ Rewrites the file at |implementation| by adding “__attribute__((unused))” to
+ the definition of any structure typedefed as “__Reply” by searching for the
+ pattern unique to those structure definitions. These structures are in fact
+ unused in the user implementation file, and this will trigger a
+ -Wunused-local-typedefs warning in gcc unless removed or marked with the
+ “unused” attribute.
+ """
+
+ file = open(implementation, 'r+')
+ contents = file.read()
+
+ pattern = re.compile('^(\t} __Reply);$', re.MULTILINE)
+ contents = pattern.sub(r'\1 __attribute__((unused));', contents)
+
+ file.seek(0)
+ file.truncate()
+ file.write(contents)
+ file.close()
+
+def FixServerImplementation(implementation):
+ """Rewrites a MIG-generated server implementation (.c) file.
+
+ Rewrites the file at |implementation| by replacing “mig_internal” with
+ “mig_external” on functions that begin with “__MIG_check__”. This makes these
+ functions available to other callers outside this file from a linkage
+ perspective. It then returns, as a list of lines, declarations that can be
+ added to a header file, so that other files that include that header file will
+ have access to these declarations from a compilation perspective.
+ """
+
+ file = open(implementation, 'r+')
+ contents = file.read()
+
+ # Find interesting declarations.
+ declaration_pattern = \
+ re.compile('^mig_internal (kern_return_t __MIG_check__.*)$',
+ re.MULTILINE)
+ declarations = declaration_pattern.findall(contents)
+
+ # Remove “__attribute__((__unused__))” from the declarations, and call them
+ # “mig_external” or “extern” depending on whether “mig_external” is defined.
+ attribute_pattern = re.compile(r'__attribute__\(\(__unused__\)\) ')
+ declarations = ['#ifdef mig_external\nmig_external\n#else\nextern\n#endif\n' +
+ attribute_pattern.sub('', x) +
+ ';\n' for x in declarations]
+
+ # Rewrite the declarations in this file as “mig_external”.
+ contents = declaration_pattern.sub(r'mig_external \1', contents);
+
+ file.seek(0)
+ file.truncate()
+ file.write(contents)
+ file.close()
+ return declarations
+
+def FixHeader(header, declarations=[]):
+ """Rewrites a MIG-generated header (.h) file.
+
+ Rewrites the file at |header| by placing it inside an “extern "C"” block, so
+ that it declares things properly when included by a C++ compilation unit.
+ |declarations| can be a list of additional declarations to place inside the
+ “extern "C"” block after the original contents of |header|.
+ """
+
+ file = open(header, 'r+')
+ contents = file.read()
+ declarations_text = ''.join(declarations)
+ contents = '''\
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+%s
+%s
+#ifdef __cplusplus
+}
+#endif
+''' % (contents, declarations_text)
+ file.seek(0)
+ file.truncate()
+ file.write(contents)
+ file.close()
+
+def main(args):
+ assert len(args) == 5
+ (defs_file, user_c, server_c, user_h, server_h) = args
+ subprocess.check_call(['mig',
+ '-user', user_c,
+ '-server', server_c,
+ '-header', user_h,
+ '-sheader', server_h,
+ defs_file])
+ FixUserImplementation(user_c)
+ server_declarations = FixServerImplementation(server_c)
+ FixHeader(user_h)
+ FixHeader(server_h, server_declarations)
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/notify_server.cc b/chromium/third_party/crashpad/crashpad/util/mach/notify_server.cc
new file mode 100644
index 00000000000..05495c65dd3
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/notify_server.cc
@@ -0,0 +1,245 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/notify_server.h"
+
+#include "base/logging.h"
+#include "util/mach/notifyServer.h"
+#include "util/mach/mach_message.h"
+
+extern "C" {
+
+// These five functions are not used, and are in fact obsoleted by the other
+// functionality implemented in this file. The standard MIG-generated
+// notify_server() (in notifyServer.c) server dispatch routine usable with the
+// standard mach_msg_server() function calls out to this function.
+// notify_server() is unused and is replaced by the more flexible NotifyServer,
+// but the linker still needs to see these five function definitions.
+
+kern_return_t do_mach_notify_port_deleted(notify_port_t notify,
+ mach_port_name_t name) {
+ NOTREACHED();
+ return KERN_FAILURE;
+}
+
+kern_return_t do_mach_notify_port_destroyed(notify_port_t notify,
+ mach_port_t rights) {
+ NOTREACHED();
+ return KERN_FAILURE;
+}
+
+kern_return_t do_mach_notify_no_senders(notify_port_t notify,
+ mach_port_mscount_t mscount) {
+ NOTREACHED();
+ return KERN_FAILURE;
+}
+
+kern_return_t do_mach_notify_send_once(notify_port_t notify) {
+ NOTREACHED();
+ return KERN_FAILURE;
+}
+
+kern_return_t do_mach_notify_dead_name(notify_port_t notify,
+ mach_port_name_t name) {
+ NOTREACHED();
+ return KERN_FAILURE;
+}
+
+} // extern "C"
+
+namespace {
+
+// The MIG-generated __MIG_check__Request__*() functions are not declared as
+// accepting const data, but they could have been because they in fact do not
+// modify the data. These wrapper functions are provided to bridge the const gap
+// between the code in this file, which is const-correct and treats request
+// message data as const, and the generated functions.
+
+kern_return_t MIGCheckRequestMachNotifyPortDeleted(
+ const __Request__mach_notify_port_deleted_t* in_request) {
+ using Request = __Request__mach_notify_port_deleted_t;
+ return __MIG_check__Request__mach_notify_port_deleted_t(
+ const_cast<Request*>(in_request));
+}
+
+kern_return_t MIGCheckRequestMachNotifyPortDestroyed(
+ const __Request__mach_notify_port_destroyed_t* in_request) {
+ using Request = __Request__mach_notify_port_destroyed_t;
+ return __MIG_check__Request__mach_notify_port_destroyed_t(
+ const_cast<Request*>(in_request));
+}
+
+kern_return_t MIGCheckRequestMachNotifyNoSenders(
+ const __Request__mach_notify_no_senders_t* in_request) {
+ using Request = __Request__mach_notify_no_senders_t;
+ return __MIG_check__Request__mach_notify_no_senders_t(
+ const_cast<Request*>(in_request));
+}
+
+kern_return_t MIGCheckRequestMachNotifySendOnce(
+ const __Request__mach_notify_send_once_t* in_request) {
+ using Request = __Request__mach_notify_send_once_t;
+ return __MIG_check__Request__mach_notify_send_once_t(
+ const_cast<Request*>(in_request));
+}
+
+kern_return_t MIGCheckRequestMachNotifyDeadName(
+ const __Request__mach_notify_dead_name_t* in_request) {
+ using Request = __Request__mach_notify_dead_name_t;
+ return __MIG_check__Request__mach_notify_dead_name_t(
+ const_cast<Request*>(in_request));
+}
+
+} // namespace
+
+namespace crashpad {
+
+NotifyServer::NotifyServer(NotifyServer::Interface* interface)
+ : MachMessageServer::Interface(),
+ interface_(interface) {
+}
+
+bool NotifyServer::MachMessageServerFunction(
+ const mach_msg_header_t* in_header,
+ mach_msg_header_t* out_header,
+ bool* destroy_complex_request) {
+ PrepareMIGReplyFromRequest(in_header, out_header);
+
+ const mach_msg_trailer_t* in_trailer =
+ MachMessageTrailerFromHeader(in_header);
+
+ switch (in_header->msgh_id) {
+ case MACH_NOTIFY_PORT_DELETED: {
+ // mach_notify_port_deleted(), do_mach_notify_port_deleted().
+ using Request = __Request__mach_notify_port_deleted_t;
+ const Request* in_request = reinterpret_cast<const Request*>(in_header);
+ kern_return_t kr = MIGCheckRequestMachNotifyPortDeleted(in_request);
+ if (kr != MACH_MSG_SUCCESS) {
+ SetMIGReplyError(out_header, kr);
+ return true;
+ }
+
+ using Reply = __Reply__mach_notify_port_deleted_t;
+ Reply* out_reply = reinterpret_cast<Reply*>(out_header);
+ out_reply->RetCode =
+ interface_->DoMachNotifyPortDeleted(in_header->msgh_local_port,
+ in_request->name,
+ in_trailer);
+ return true;
+ }
+
+ case MACH_NOTIFY_PORT_DESTROYED: {
+ // mach_notify_port_destroyed(), do_mach_notify_port_destroyed().
+ using Request = __Request__mach_notify_port_destroyed_t;
+ const Request* in_request = reinterpret_cast<const Request*>(in_header);
+ kern_return_t kr = MIGCheckRequestMachNotifyPortDestroyed(in_request);
+ if (kr != MACH_MSG_SUCCESS) {
+ SetMIGReplyError(out_header, kr);
+ return true;
+ }
+
+ using Reply = __Reply__mach_notify_port_destroyed_t;
+ Reply* out_reply = reinterpret_cast<Reply*>(out_header);
+ out_reply->RetCode =
+ interface_->DoMachNotifyPortDestroyed(in_header->msgh_local_port,
+ in_request->rights.name,
+ in_trailer,
+ destroy_complex_request);
+ return true;
+ }
+
+ case MACH_NOTIFY_NO_SENDERS: {
+ // mach_notify_no_senders(), do_mach_notify_no_senders().
+ using Request = __Request__mach_notify_no_senders_t;
+ const Request* in_request = reinterpret_cast<const Request*>(in_header);
+ kern_return_t kr = MIGCheckRequestMachNotifyNoSenders(in_request);
+ if (kr != MACH_MSG_SUCCESS) {
+ SetMIGReplyError(out_header, kr);
+ return true;
+ }
+
+ using Reply = __Reply__mach_notify_no_senders_t;
+ Reply* out_reply = reinterpret_cast<Reply*>(out_header);
+ out_reply->RetCode =
+ interface_->DoMachNotifyNoSenders(in_header->msgh_local_port,
+ in_request->mscount,
+ in_trailer);
+ return true;
+ }
+
+ case MACH_NOTIFY_SEND_ONCE: {
+ // mach_notify_send_once(), do_mach_notify_send_once().
+ using Request = __Request__mach_notify_send_once_t;
+ const Request* in_request = reinterpret_cast<const Request*>(in_header);
+ kern_return_t kr = MIGCheckRequestMachNotifySendOnce(in_request);
+ if (kr != MACH_MSG_SUCCESS) {
+ SetMIGReplyError(out_header, kr);
+ return true;
+ }
+
+ using Reply = __Reply__mach_notify_send_once_t;
+ Reply* out_reply = reinterpret_cast<Reply*>(out_header);
+ out_reply->RetCode =
+ interface_->DoMachNotifySendOnce(in_header->msgh_local_port,
+ in_trailer);
+ return true;
+ }
+
+ case MACH_NOTIFY_DEAD_NAME: {
+ // mach_notify_dead_name(), do_mach_notify_dead_name().
+ using Request = __Request__mach_notify_dead_name_t;
+ const Request* in_request = reinterpret_cast<const Request*>(in_header);
+ kern_return_t kr = MIGCheckRequestMachNotifyDeadName(in_request);
+ if (kr != MACH_MSG_SUCCESS) {
+ SetMIGReplyError(out_header, kr);
+ return true;
+ }
+
+ using Reply = __Reply__mach_notify_dead_name_t;
+ Reply* out_reply = reinterpret_cast<Reply*>(out_header);
+ out_reply->RetCode =
+ interface_->DoMachNotifyDeadName(in_header->msgh_local_port,
+ in_request->name,
+ in_trailer);
+ return true;
+ }
+
+ default: {
+ SetMIGReplyError(out_header, MIG_BAD_ID);
+ return false;
+ }
+ }
+}
+
+std::set<mach_msg_id_t> NotifyServer::MachMessageServerRequestIDs() {
+ const mach_msg_id_t request_ids[] = {
+ MACH_NOTIFY_PORT_DELETED,
+ MACH_NOTIFY_PORT_DESTROYED,
+ MACH_NOTIFY_NO_SENDERS,
+ MACH_NOTIFY_SEND_ONCE,
+ MACH_NOTIFY_DEAD_NAME,
+ };
+ return std::set<mach_msg_id_t>(&request_ids[0],
+ &request_ids[arraysize(request_ids)]);
+}
+
+mach_msg_size_t NotifyServer::MachMessageServerRequestSize() {
+ return sizeof(__RequestUnion__do_notify_subsystem);
+}
+
+mach_msg_size_t NotifyServer::MachMessageServerReplySize() {
+ return sizeof(__ReplyUnion__do_notify_subsystem);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/notify_server.h b/chromium/third_party/crashpad/crashpad/util/mach/notify_server.h
new file mode 100644
index 00000000000..311ad3a0af5
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/notify_server.h
@@ -0,0 +1,193 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MACH_NOTIFY_SERVER_H_
+#define CRASHPAD_UTIL_MACH_NOTIFY_SERVER_H_
+
+#include <mach/mach.h>
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "util/mach/mach_message_server.h"
+
+namespace crashpad {
+
+//! \brief A server interface for the `notify` Mach subsystem.
+//!
+//! The <a
+//! href="https://lists.apple.com/archives/darwin-development/2001/Sep/msg00451.html">mach
+//! port notifications</a> thread on the <a
+//! href="https://lists.apple.com/archives/darwin-development/">darwin-development</a>
+//! mailing list (now known as <a
+//! href="https://lists.apple.com/mailman/listinfo/darwin-dev">darwin-dev</a>)
+//! is good background for the various notification types.
+class NotifyServer : public MachMessageServer::Interface {
+ public:
+ //! \brief An interface that the different request messages that are a part of
+ //! the `notify` Mach subsystem can be dispatched to.
+ class Interface {
+ public:
+ //! \brief Handles port-deleted notifications sent by
+ //! `mach_notify_port_deleted()`.
+ //!
+ //! A port-deleted notification is generated when a port with a dead-name
+ //! notification request is destroyed and the port name becomes available
+ //! for reuse.
+ //!
+ //! This behaves equivalently to a `do_mach_notify_port_deleted()` function
+ //! used with `notify_server()`.
+ //!
+ //! \param[in] notify The Mach port that the notification was sent to.
+ //! \param[in] name The name that formerly referenced the deleted port. When
+ //! this method is called, \a name no longer corresponds to the port
+ //! that has been deleted, and may be reused for another purpose.
+ //! \param[in] trailer The trailer received with the notification message.
+ virtual kern_return_t DoMachNotifyPortDeleted(
+ notify_port_t notify,
+ mach_port_name_t name,
+ const mach_msg_trailer_t* trailer) = 0;
+
+ //! \brief Handles port-destroyed notifications sent by
+ //! `mach_notify_port_destroyed()`.
+ //!
+ //! A port-destroyed notification is generated when a receive right with a
+ //! port-destroyed notification request is destroyed. Rather than destroying
+ //! the receive right, it is transferred via this notification’s \a rights
+ //! parameter.
+ //!
+ //! This behaves equivalently to a `do_mach_notify_port_destroyed()`
+ //! function used with `notify_server()`.
+ //!
+ //! \param[in] notify The Mach port that the notification was sent to.
+ //! \param[in] rights A receive right for the port that would have been
+ //! destroyed. The callee takes ownership of this port, however, if the
+ //! callee does not wish to take ownership, it may set \a
+ //! destroy_request to `true`.
+ //! \param[in] trailer The trailer received with the notification message.
+ //! \param[out] destroy_request `true` if the request message is to be
+ //! destroyed even when this method returns success. See
+ //! MachMessageServer::Interface.
+ virtual kern_return_t DoMachNotifyPortDestroyed(
+ notify_port_t notify,
+ mach_port_t rights,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_request) = 0;
+
+ //! \brief Handles no-senders notifications sent by
+ //! `mach_notify_no_senders()`.
+ //!
+ //! A no-senders notification is generated when a receive right with a
+ //! no-senders notification request loses its last corresponding send right.
+ //!
+ //! This behaves equivalently to a `do_mach_notify_no_senders()` function
+ //! used with `notify_server()`.
+ //!
+ //! \param[in] notify The Mach port that the notification was sent to.
+ //! \param[in] mscount The value of the sender-less port’s make-send count
+ //! at the time the notification was generated.
+ //! \param[in] trailer The trailer received with the notification message.
+ virtual kern_return_t DoMachNotifyNoSenders(
+ notify_port_t notify,
+ mach_port_mscount_t mscount,
+ const mach_msg_trailer_t* trailer) = 0;
+
+ //! \brief Handles send-once notifications sent by
+ //! `mach_notify_send_once()`.
+ //!
+ //! A send-once notification is generated when a send-once right is
+ //! destroyed without being used.
+ //!
+ //! This behaves equivalently to a `do_mach_notify_send_once()` function
+ //! used with `notify_server()`.
+ //!
+ //! \param[in] notify The Mach port that the notification was sent to.
+ //! \param[in] trailer The trailer received with the notification message.
+ //!
+ //! \note Unlike the other notifications in the `notify` subsystem,
+ //! send-once notifications are not generated as a result of a
+ //! notification request, but are generated any time a send-once right
+ //! is destroyed rather than being used. The notification is sent via
+ //! the send-once right to its receiver. These notifications are more
+ //! useful for clients, not servers. Send-once notifications are
+ //! normally handled by MIG-generated client routines, which make
+ //! send-once rights for their reply ports and interpret send-once
+ //! notifications as a signal that there will be no reply. Although not
+ //! expected to be primarily useful for servers, this method is provided
+ //! because send-once notifications are defined as a part of the
+ //! `notify` subsystem.
+ virtual kern_return_t DoMachNotifySendOnce(
+ notify_port_t notify,
+ const mach_msg_trailer_t* trailer) = 0;
+
+ //! \brief Handles dead-name notifications sent by
+ //! `mach_notify_dead_name()`.
+ //!
+ //! A dead-name notification is generated when a port with a dead-name
+ //! notification request is destroyed and the right becomes a dead name.
+ //!
+ //! This behaves equivalently to a `do_mach_notify_dead_name()` function
+ //! used with `notify_server()`.
+ //!
+ //! \param[in] notify The Mach port that the notification was sent to.
+ //! \param[in] name The dead name. Although this is transferred as a
+ //! `mach_port_name_t` and not a `mach_port_t`, the callee assumes an
+ //! additional reference to this port when this method is called. See
+ //! the note below.
+ //! \param[in] trailer The trailer received with the notification message.
+ //!
+ //! \note When a dead-name notification is generated, the user reference
+ //! count of the dead name is incremented. A send right with one
+ //! reference that becomes a dead name will have one dead-name
+ //! reference, and the dead-name notification will add another dead-name
+ //! reference, for a total of 2. DoMachNotifyDeadName() implementations
+ //! must take care to deallocate this extra reference. There is no \a
+ //! destroy_request parameter to simplify this operation because
+ //! dead-name notifications carry a port name only (\a name is of type
+ //! `mach_port_name_t`) without transferring port rights, and are thus
+ //! not complex Mach messages.
+ virtual kern_return_t DoMachNotifyDeadName(
+ notify_port_t notify,
+ mach_port_name_t name,
+ const mach_msg_trailer_t* trailer) = 0;
+
+ protected:
+ ~Interface() {}
+ };
+
+ //! \brief Constructs an object of this class.
+ //!
+ //! \param[in] interface The interface to dispatch requests to. Weak.
+ explicit NotifyServer(Interface* interface);
+
+ // MachMessageServer::Interface:
+
+ bool MachMessageServerFunction(const mach_msg_header_t* in_header,
+ mach_msg_header_t* out_header,
+ bool* destroy_complex_request) override;
+
+ std::set<mach_msg_id_t> MachMessageServerRequestIDs() override;
+
+ mach_msg_size_t MachMessageServerRequestSize() override;
+ mach_msg_size_t MachMessageServerReplySize() override;
+
+ private:
+ Interface* interface_; // weak
+
+ DISALLOW_COPY_AND_ASSIGN(NotifyServer);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MACH_NOTIFY_SERVER_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/notify_server_test.cc b/chromium/third_party/crashpad/crashpad/util/mach/notify_server_test.cc
new file mode 100644
index 00000000000..7a19a74238a
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/notify_server_test.cc
@@ -0,0 +1,556 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/notify_server.h"
+
+#include "base/compiler_specific.h"
+#include "base/mac/scoped_mach_port.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "test/mac/mach_errors.h"
+#include "util/mach/mach_extensions.h"
+#include "util/mach/mach_message.h"
+#include "util/mach/mach_message_server.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+using testing::AllOf;
+using testing::Eq;
+using testing::Invoke;
+using testing::Pointee;
+using testing::ResultOf;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::StrictMock;
+using testing::WithArg;
+
+//! \brief Adds a send right to an existing receive right.
+//!
+//! \param[in] receive_right The receive right to add a send right to.
+//!
+//! \return The send right, which will have the same name as the receive right.
+//! On failure, `MACH_PORT_NULL` with a gtest failure added.
+mach_port_t SendRightFromReceiveRight(mach_port_t receive_right) {
+ kern_return_t kr = mach_port_insert_right(
+ mach_task_self(), receive_right, receive_right, MACH_MSG_TYPE_MAKE_SEND);
+ if (kr != KERN_SUCCESS) {
+ EXPECT_EQ(KERN_SUCCESS, kr)
+ << MachErrorMessage(kr, "mach_port_insert_right");
+ return MACH_PORT_NULL;
+ }
+
+ return receive_right;
+}
+
+//! \brief Extracts a send-once right from a receive right.
+//!
+//! \param[in] receive_right The receive right to make a send-once right from.
+//!
+//! \return The send-once right. On failure, `MACH_PORT_NULL` with a gtest
+//! failure added.
+mach_port_t SendOnceRightFromReceiveRight(mach_port_t receive_right) {
+ mach_port_t send_once_right;
+ mach_msg_type_name_t acquired_type;
+ kern_return_t kr = mach_port_extract_right(mach_task_self(),
+ receive_right,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE,
+ &send_once_right,
+ &acquired_type);
+ if (kr != KERN_SUCCESS) {
+ EXPECT_EQ(KERN_SUCCESS, kr)
+ << MachErrorMessage(kr, "mach_port_extract_right");
+ return MACH_PORT_NULL;
+ }
+
+ EXPECT_EQ(implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND_ONCE),
+ acquired_type);
+
+ return send_once_right;
+}
+
+//! \brief Deallocates a Mach port by calling `mach_port_deallocate()`.
+//!
+//! This function exists to adapt `mach_port_deallocate()` to a function that
+//! accepts a single argument and has no return value. It can be used with the
+//! testing::Invoke() gmock action.
+//!
+//! On failure, a gtest failure will be added.
+void MachPortDeallocate(mach_port_t port) {
+ kern_return_t kr = mach_port_deallocate(mach_task_self(), port);
+ EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_deallocate");
+}
+
+//! \brief Determines whether a specific right is held for a Mach port.
+//!
+//! \param[in] port The port to check for a right.
+//! \param[in] right The right to check for.
+//!
+//! \return `true` if \a port has \a right, `false` otherwise. On faliure,
+//! `false` with a gtest failure added.
+bool IsRight(mach_port_t port, mach_port_type_t right) {
+ mach_port_type_t type;
+ kern_return_t kr = mach_port_type(mach_task_self(), port, &type);
+ if (kr != KERN_SUCCESS) {
+ EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_type");
+ return false;
+ }
+
+ return type & right;
+}
+
+//! \brief Determines whether a receive right is held for a Mach port.
+//!
+//! This is a special single-argument form of IsRight() for ease of use in a
+//! gmock matcher.
+//!
+//! \param[in] port The port to check for a receive right.
+//!
+//! \return `true` if a receive right is held, `false` otherwise. On faliure,
+//! `false` with a gtest failure added.
+bool IsReceiveRight(mach_port_t port) {
+ return IsRight(port, MACH_PORT_TYPE_RECEIVE);
+}
+
+//! \brief Returns the user reference count for port rights.
+//!
+//! \param[in] port The port whose user reference count should be returned.
+//! \param[in] right The port right to return the user reference count for.
+//!
+//! \return The user reference count for the specified port and right. On
+//! failure, `-1` with a gtest failure added.
+mach_port_urefs_t RightRefCount(mach_port_t port, mach_port_right_t right) {
+ mach_port_urefs_t refs;
+ kern_return_t kr = mach_port_get_refs(mach_task_self(), port, right, &refs);
+ if (kr != KERN_SUCCESS) {
+ EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_get_refs");
+ return -1;
+ }
+
+ return refs;
+}
+
+//! \brief Returns the user reference count for a port’s dead-name rights.
+//!
+//! This is a special single-argument form of RightRefCount() for ease of use in
+//! a gmock matcher.
+//!
+//! \param[in] port The port whose dead-name user reference count should be
+//! returned.
+//!
+//! \return The user reference count for the port’s dead-name rights. On
+//! failure, `-1` with a gtest failure added.
+mach_port_urefs_t DeadNameRightRefCount(mach_port_t port) {
+ return RightRefCount(port, MACH_PORT_RIGHT_DEAD_NAME);
+}
+
+class NotifyServerTestBase : public testing::Test,
+ public NotifyServer::Interface {
+ public:
+ // NotifyServer::Interface:
+
+ MOCK_METHOD3(DoMachNotifyPortDeleted,
+ kern_return_t(notify_port_t notify,
+ mach_port_name_t name,
+ const mach_msg_trailer_t* trailer));
+
+ MOCK_METHOD4(DoMachNotifyPortDestroyed,
+ kern_return_t(notify_port_t notify,
+ mach_port_t rights,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_request));
+
+ MOCK_METHOD3(DoMachNotifyNoSenders,
+ kern_return_t(notify_port_t notify,
+ mach_port_mscount_t mscount,
+ const mach_msg_trailer_t* trailer));
+
+ MOCK_METHOD2(DoMachNotifySendOnce,
+ kern_return_t(notify_port_t notify,
+ const mach_msg_trailer_t* trailer));
+
+ MOCK_METHOD3(DoMachNotifyDeadName,
+ kern_return_t(notify_port_t notify,
+ mach_port_name_t name,
+ const mach_msg_trailer_t* trailer));
+
+ protected:
+ NotifyServerTestBase() : testing::Test(), NotifyServer::Interface() {}
+
+ ~NotifyServerTestBase() override {}
+
+ //! \brief Requests a Mach port notification.
+ //!
+ //! \a name, \a variant, and \a sync are passed as-is to
+ //! `mach_port_request_notification()`. The notification will be sent to a
+ //! send-once right made from ServerPort(). Any previous send right for the
+ //! notification will be deallocated.
+ //!
+ //! \return `true` on success, `false` on failure with a gtest failure added.
+ bool RequestMachPortNotification(mach_port_t name,
+ mach_msg_id_t variant,
+ mach_port_mscount_t sync) {
+ mach_port_t previous;
+ kern_return_t kr =
+ mach_port_request_notification(mach_task_self(),
+ name,
+ variant,
+ sync,
+ ServerPort(),
+ MACH_MSG_TYPE_MAKE_SEND_ONCE,
+ &previous);
+ if (kr != KERN_SUCCESS) {
+ EXPECT_EQ(KERN_SUCCESS, kr)
+ << MachErrorMessage(kr, "mach_port_request_notification");
+ return false;
+ }
+
+ base::mac::ScopedMachSendRight previous_owner(previous);
+ EXPECT_EQ(kMachPortNull, previous);
+
+ return true;
+ }
+
+ //! \brief Runs a NotifyServer Mach message server.
+ //!
+ //! The server will listen on ServerPort() in persistent nonblocking mode, and
+ //! dispatch received messages to the appropriate NotifyServer::Interface
+ //! method. gmock expectations check that the proper method, if any, is called
+ //! exactly once, and that no undesired methods are called.
+ //!
+ //! MachMessageServer::Run() is expected to return `MACH_RCV_TIMED_OUT`,
+ //! because it runs in persistent nonblocking mode. If it returns anything
+ //! else, a gtest assertion is added.
+ void RunServer() {
+ NotifyServer notify_server(this);
+ mach_msg_return_t mr =
+ MachMessageServer::Run(&notify_server,
+ ServerPort(),
+ kMachMessageReceiveAuditTrailer,
+ MachMessageServer::kPersistent,
+ MachMessageServer::kReceiveLargeError,
+ kMachMessageTimeoutNonblocking);
+ ASSERT_EQ(MACH_RCV_TIMED_OUT, mr)
+ << MachErrorMessage(mr, "MachMessageServer::Run");
+ }
+
+ //! \brief Returns the receive right to be used for the server.
+ //!
+ //! This receive right is created lazily on a per-test basis. It is destroyed
+ //! by TearDown() at the conclusion of each test.
+ //!
+ //! \return The server port receive right, creating it if one has not yet been
+ //! established for the current test. On failure, returns `MACH_PORT_NULL`
+ //! with a gtest failure added.
+ mach_port_t ServerPort() {
+ if (!server_port_) {
+ server_port_.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
+ EXPECT_NE(kMachPortNull, server_port_);
+ }
+
+ return server_port_;
+ }
+
+ // testing::Test:
+ void TearDown() override {
+ server_port_.reset();
+ }
+
+ private:
+ base::mac::ScopedMachReceiveRight server_port_;
+
+ DISALLOW_COPY_AND_ASSIGN(NotifyServerTestBase);
+};
+
+using NotifyServerTest = StrictMock<NotifyServerTestBase>;
+
+TEST_F(NotifyServerTest, Basic) {
+ NotifyServer server(this);
+
+ std::set<mach_msg_id_t> expect_request_ids;
+ expect_request_ids.insert(MACH_NOTIFY_PORT_DELETED);
+ expect_request_ids.insert(MACH_NOTIFY_PORT_DESTROYED);
+ expect_request_ids.insert(MACH_NOTIFY_NO_SENDERS);
+ expect_request_ids.insert(MACH_NOTIFY_SEND_ONCE);
+ expect_request_ids.insert(MACH_NOTIFY_DEAD_NAME);
+ EXPECT_EQ(expect_request_ids, server.MachMessageServerRequestIDs());
+
+ // The port-destroyed notification is the largest request message in the
+ // subsystem. <mach/notify.h> defines the same structure, but with a basic
+ // trailer, so use offsetof to get the size of the basic structure without any
+ // trailer.
+ EXPECT_EQ(offsetof(mach_port_destroyed_notification_t, trailer),
+ server.MachMessageServerRequestSize());
+
+ mig_reply_error_t reply;
+ EXPECT_EQ(sizeof(reply), server.MachMessageServerReplySize());
+}
+
+// When no notifications are requested, nothing should happen.
+TEST_F(NotifyServerTest, NoNotification) {
+ RunServer();
+}
+
+// When a send-once right with a dead-name notification request is deallocated,
+// a port-deleted notification should be generated.
+TEST_F(NotifyServerTest, MachNotifyPortDeleted) {
+ base::mac::ScopedMachReceiveRight receive_right(
+ NewMachPort(MACH_PORT_RIGHT_RECEIVE));
+ ASSERT_NE(kMachPortNull, receive_right);
+
+ base::mac::ScopedMachSendRight send_once_right(
+ SendOnceRightFromReceiveRight(receive_right));
+ ASSERT_NE(kMachPortNull, send_once_right);
+
+ ASSERT_TRUE(
+ RequestMachPortNotification(send_once_right, MACH_NOTIFY_DEAD_NAME, 0));
+
+ EXPECT_CALL(
+ *this,
+ DoMachNotifyPortDeleted(ServerPort(),
+ send_once_right.get(),
+ ResultOf(AuditPIDFromMachMessageTrailer, 0)))
+ .WillOnce(Return(MIG_NO_REPLY))
+ .RetiresOnSaturation();
+
+ send_once_right.reset();
+
+ RunServer();
+}
+
+// When a receive right with a port-destroyed notification request is destroyed,
+// a port-destroyed notification should be generated.
+TEST_F(NotifyServerTest, MachNotifyPortDestroyed) {
+ base::mac::ScopedMachReceiveRight receive_right(
+ NewMachPort(MACH_PORT_RIGHT_RECEIVE));
+ ASSERT_NE(kMachPortNull, receive_right);
+
+ ASSERT_TRUE(RequestMachPortNotification(
+ receive_right, MACH_NOTIFY_PORT_DESTROYED, 0));
+
+ EXPECT_CALL(
+ *this,
+ DoMachNotifyPortDestroyed(ServerPort(),
+ ResultOf(IsReceiveRight, true),
+ ResultOf(AuditPIDFromMachMessageTrailer, 0),
+ Pointee(Eq(false))))
+ .WillOnce(DoAll(SetArgPointee<3>(true), Return(MIG_NO_REPLY)))
+ .RetiresOnSaturation();
+
+ receive_right.reset();
+
+ RunServer();
+}
+
+// When a receive right with a port-destroyed notification request is not
+// destroyed, no port-destroyed notification should be generated.
+TEST_F(NotifyServerTest, MachNotifyPortDestroyed_NoNotification) {
+ base::mac::ScopedMachReceiveRight receive_right(
+ NewMachPort(MACH_PORT_RIGHT_RECEIVE));
+ ASSERT_NE(kMachPortNull, receive_right);
+
+ ASSERT_TRUE(RequestMachPortNotification(
+ receive_right, MACH_NOTIFY_PORT_DESTROYED, 0));
+
+ RunServer();
+}
+
+// When a no-senders notification request is registered for a receive right with
+// no senders, a no-senders notification should be generated.
+TEST_F(NotifyServerTest, MachNotifyNoSenders_NoSendRight) {
+ base::mac::ScopedMachReceiveRight receive_right(
+ NewMachPort(MACH_PORT_RIGHT_RECEIVE));
+ ASSERT_NE(kMachPortNull, receive_right);
+
+ ASSERT_TRUE(
+ RequestMachPortNotification(receive_right, MACH_NOTIFY_NO_SENDERS, 0));
+
+ EXPECT_CALL(*this,
+ DoMachNotifyNoSenders(
+ ServerPort(), 0, ResultOf(AuditPIDFromMachMessageTrailer, 0)))
+ .WillOnce(Return(MIG_NO_REPLY))
+ .RetiresOnSaturation();
+
+ RunServer();
+}
+
+// When the last send right corresponding to a receive right with a no-senders
+// notification request is deallocated, a no-senders notification should be
+// generated.
+TEST_F(NotifyServerTest, MachNotifyNoSenders_SendRightDeallocated) {
+ base::mac::ScopedMachReceiveRight receive_right(
+ NewMachPort(MACH_PORT_RIGHT_RECEIVE));
+ ASSERT_NE(kMachPortNull, receive_right);
+
+ base::mac::ScopedMachSendRight send_right(
+ SendRightFromReceiveRight(receive_right));
+ ASSERT_NE(kMachPortNull, send_right);
+
+ ASSERT_TRUE(
+ RequestMachPortNotification(receive_right, MACH_NOTIFY_NO_SENDERS, 1));
+
+ EXPECT_CALL(*this,
+ DoMachNotifyNoSenders(
+ ServerPort(), 1, ResultOf(AuditPIDFromMachMessageTrailer, 0)))
+ .WillOnce(Return(MIG_NO_REPLY))
+ .RetiresOnSaturation();
+
+ send_right.reset();
+
+ RunServer();
+}
+
+// When the a receive right with a no-senders notification request never loses
+// all senders, no no-senders notification should be generated.
+TEST_F(NotifyServerTest, MachNotifyNoSenders_NoNotification) {
+ base::mac::ScopedMachReceiveRight receive_right(
+ NewMachPort(MACH_PORT_RIGHT_RECEIVE));
+ ASSERT_NE(kMachPortNull, receive_right);
+
+ base::mac::ScopedMachSendRight send_right_0(
+ SendRightFromReceiveRight(receive_right));
+ ASSERT_NE(kMachPortNull, send_right_0);
+
+ base::mac::ScopedMachSendRight send_right_1(
+ SendRightFromReceiveRight(receive_right));
+ ASSERT_NE(kMachPortNull, send_right_1);
+
+ ASSERT_TRUE(
+ RequestMachPortNotification(receive_right, MACH_NOTIFY_NO_SENDERS, 1));
+
+ send_right_1.reset();
+
+ RunServer();
+
+ EXPECT_EQ(1u, RightRefCount(receive_right, MACH_PORT_RIGHT_RECEIVE));
+ EXPECT_EQ(1u, RightRefCount(receive_right, MACH_PORT_RIGHT_SEND));
+}
+
+// When a send-once right is deallocated without being used, a send-once
+// notification notification should be sent via the send-once right.
+TEST_F(NotifyServerTest, MachNotifySendOnce_ExplicitDeallocation) {
+ base::mac::ScopedMachSendRight send_once_right(
+ SendOnceRightFromReceiveRight(ServerPort()));
+ ASSERT_NE(kMachPortNull, send_once_right);
+
+ EXPECT_CALL(*this,
+ DoMachNotifySendOnce(ServerPort(),
+ ResultOf(AuditPIDFromMachMessageTrailer, 0)))
+ .WillOnce(Return(MIG_NO_REPLY))
+ .RetiresOnSaturation();
+
+ send_once_right.reset();
+
+ RunServer();
+}
+
+// When a send-once right is sent to a receiver that never dequeues the message,
+// the send-once right is destroyed, and a send-once notification should appear
+// on the reply port.
+TEST_F(NotifyServerTest, MachNotifySendOnce_ImplicitDeallocation) {
+ base::mac::ScopedMachReceiveRight receive_right(
+ NewMachPort(MACH_PORT_RIGHT_RECEIVE));
+ ASSERT_NE(kMachPortNull, receive_right);
+
+ mach_msg_empty_send_t message = {};
+ message.header.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
+ message.header.msgh_size = sizeof(message);
+ message.header.msgh_remote_port = receive_right;
+ message.header.msgh_local_port = ServerPort();
+ mach_msg_return_t mr = mach_msg(&message.header,
+ MACH_SEND_MSG | MACH_SEND_TIMEOUT,
+ message.header.msgh_size,
+ 0,
+ MACH_PORT_NULL,
+ 0,
+ MACH_PORT_NULL);
+ ASSERT_EQ(MACH_MSG_SUCCESS, mr) << MachErrorMessage(mr, "mach_msg");
+
+ EXPECT_CALL(*this,
+ DoMachNotifySendOnce(ServerPort(),
+ ResultOf(AuditPIDFromMachMessageTrailer, 0)))
+ .WillOnce(Return(MIG_NO_REPLY))
+ .RetiresOnSaturation();
+
+ receive_right.reset();
+
+ RunServer();
+}
+
+// When the receive right corresponding to a send-once right with a dead-name
+// notification request is destroyed, a dead-name notification should be
+// generated.
+TEST_F(NotifyServerTest, MachNotifyDeadName) {
+ base::mac::ScopedMachReceiveRight receive_right(
+ NewMachPort(MACH_PORT_RIGHT_RECEIVE));
+ ASSERT_NE(kMachPortNull, receive_right);
+
+ base::mac::ScopedMachSendRight send_once_right(
+ SendOnceRightFromReceiveRight(receive_right));
+ ASSERT_NE(kMachPortNull, send_once_right);
+
+ ASSERT_TRUE(
+ RequestMachPortNotification(send_once_right, MACH_NOTIFY_DEAD_NAME, 0));
+
+ // send_once_right becomes a dead name with the send-once right’s original
+ // user reference count of 1, but the dead-name notification increments the
+ // dead-name reference count, so it becomes 2. Take care to deallocate that
+ // reference. The original reference is managed by send_once_right_owner.
+ EXPECT_CALL(*this,
+ DoMachNotifyDeadName(ServerPort(),
+ AllOf(send_once_right.get(),
+ ResultOf(DeadNameRightRefCount, 2)),
+ ResultOf(AuditPIDFromMachMessageTrailer, 0)))
+ .WillOnce(
+ DoAll(WithArg<1>(Invoke(MachPortDeallocate)), Return(MIG_NO_REPLY)))
+ .RetiresOnSaturation();
+
+ receive_right.reset();
+
+ RunServer();
+
+ EXPECT_TRUE(IsRight(send_once_right, MACH_PORT_TYPE_DEAD_NAME));
+
+ EXPECT_EQ(0u, RightRefCount(send_once_right, MACH_PORT_RIGHT_SEND_ONCE));
+ EXPECT_EQ(1u, DeadNameRightRefCount(send_once_right));
+}
+
+// When the receive right corresponding to a send-once right with a dead-name
+// notification request is not destroyed, no dead-name notification should be
+// generated.
+TEST_F(NotifyServerTest, MachNotifyDeadName_NoNotification) {
+ base::mac::ScopedMachReceiveRight receive_right(
+ NewMachPort(MACH_PORT_RIGHT_RECEIVE));
+ ASSERT_NE(kMachPortNull, receive_right);
+
+ base::mac::ScopedMachSendRight send_once_right(
+ SendOnceRightFromReceiveRight(receive_right));
+ ASSERT_NE(kMachPortNull, send_once_right);
+
+ ASSERT_TRUE(
+ RequestMachPortNotification(send_once_right, MACH_NOTIFY_DEAD_NAME, 0));
+
+ RunServer();
+
+ EXPECT_FALSE(IsRight(send_once_right, MACH_PORT_TYPE_DEAD_NAME));
+
+ EXPECT_EQ(1u, RightRefCount(send_once_right, MACH_PORT_RIGHT_SEND_ONCE));
+ EXPECT_EQ(0u, DeadNameRightRefCount(send_once_right));
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/scoped_task_suspend.cc b/chromium/third_party/crashpad/crashpad/util/mach/scoped_task_suspend.cc
new file mode 100644
index 00000000000..abca77e302a
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/scoped_task_suspend.cc
@@ -0,0 +1,39 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/scoped_task_suspend.h"
+
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+
+namespace crashpad {
+
+ScopedTaskSuspend::ScopedTaskSuspend(task_t task) : task_(task) {
+ DCHECK_NE(task_, mach_task_self());
+
+ kern_return_t kr = task_suspend(task_);
+ if (kr != KERN_SUCCESS) {
+ task_ = TASK_NULL;
+ MACH_LOG(ERROR, kr) << "task_suspend";
+ }
+}
+
+ScopedTaskSuspend::~ScopedTaskSuspend() {
+ if (task_ != TASK_NULL) {
+ kern_return_t kr = task_resume(task_);
+ MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "task_resume";
+ }
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/scoped_task_suspend.h b/chromium/third_party/crashpad/crashpad/util/mach/scoped_task_suspend.h
new file mode 100644
index 00000000000..4b90aba0aa4
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/scoped_task_suspend.h
@@ -0,0 +1,45 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MACH_SCOPED_TASK_SUSPEND_H_
+#define CRASHPAD_UTIL_MACH_SCOPED_TASK_SUSPEND_H_
+
+#include <mach/mach.h>
+
+#include "base/basictypes.h"
+
+namespace crashpad {
+
+//! \brief Manages the suspension of another task.
+//!
+//! While an object of this class exists, the other task will be suspended. Once
+//! the object is destroyed, the other task will become eligible for resumption.
+//! Note that suspensions are counted, and the task will not actually resume
+//! unless its suspend count drops to 0.
+//!
+//! Callers should not attempt to suspend the current task (`mach_task_self()`).
+class ScopedTaskSuspend {
+ public:
+ explicit ScopedTaskSuspend(task_t task);
+ ~ScopedTaskSuspend();
+
+ private:
+ task_t task_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedTaskSuspend);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MACH_SCOPED_TASK_SUSPEND_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/scoped_task_suspend_test.cc b/chromium/third_party/crashpad/crashpad/util/mach/scoped_task_suspend_test.cc
new file mode 100644
index 00000000000..b53b83dc0c7
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/scoped_task_suspend_test.cc
@@ -0,0 +1,85 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/scoped_task_suspend.h"
+
+#include <mach/mach.h>
+
+#include "gtest/gtest.h"
+#include "test/mac/mach_errors.h"
+#include "test/mac/mach_multiprocess.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+int SuspendCount(task_t task) {
+ // As of the 10.8 SDK, the preferred routine is MACH_TASK_BASIC_INFO.
+ // TASK_BASIC_INFO_64 is equivalent and works on earlier systems.
+ task_basic_info_64 task_basic_info;
+ mach_msg_type_number_t task_basic_info_count = TASK_BASIC_INFO_64_COUNT;
+ kern_return_t kr = task_info(task,
+ TASK_BASIC_INFO_64,
+ reinterpret_cast<task_info_t>(&task_basic_info),
+ &task_basic_info_count);
+ if (kr != KERN_SUCCESS) {
+ ADD_FAILURE() << MachErrorMessage(kr, "task_info");
+ return -1;
+ }
+
+ return task_basic_info.suspend_count;
+}
+
+class ScopedTaskSuspendTest final : public MachMultiprocess {
+ public:
+ ScopedTaskSuspendTest() : MachMultiprocess() {}
+ ~ScopedTaskSuspendTest() {}
+
+ private:
+ // MachMultiprocess:
+
+ void MachMultiprocessParent() override {
+ task_t child_task = ChildTask();
+
+ EXPECT_EQ(0, SuspendCount(child_task));
+
+ {
+ ScopedTaskSuspend suspend(child_task);
+ EXPECT_EQ(1, SuspendCount(child_task));
+
+ {
+ ScopedTaskSuspend suspend_again(child_task);
+ EXPECT_EQ(2, SuspendCount(child_task));
+ }
+
+ EXPECT_EQ(1, SuspendCount(child_task));
+ }
+
+ EXPECT_EQ(0, SuspendCount(child_task));
+ }
+
+ void MachMultiprocessChild() override {
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedTaskSuspendTest);
+};
+
+TEST(ScopedTaskSuspend, ScopedTaskSuspend) {
+ ScopedTaskSuspendTest scoped_task_suspend_test;
+ scoped_task_suspend_test.Run();
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach.cc b/chromium/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach.cc
new file mode 100644
index 00000000000..f505d052cd4
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach.cc
@@ -0,0 +1,545 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/symbolic_constants_mach.h"
+
+#include <string.h>
+
+#include "base/basictypes.h"
+#include "base/strings/stringprintf.h"
+#include "util/mach/exception_behaviors.h"
+#include "util/mach/mach_extensions.h"
+#include "util/stdlib/string_number_conversion.h"
+
+namespace {
+
+const char* kExceptionNames[] = {
+ nullptr,
+
+ // sed -Ene 's/^#define[[:space:]]EXC_([[:graph:]]+)[[:space:]]+[[:digit:]]{1,2}([[:space:]]|$).*/ "\1",/p'
+ // /usr/include/mach/exception_types.h
+ "BAD_ACCESS",
+ "BAD_INSTRUCTION",
+ "ARITHMETIC",
+ "EMULATION",
+ "SOFTWARE",
+ "BREAKPOINT",
+ "SYSCALL",
+ "MACH_SYSCALL",
+ "RPC_ALERT",
+ "CRASH",
+ "RESOURCE",
+ "GUARD",
+};
+static_assert(arraysize(kExceptionNames) == EXC_TYPES_COUNT,
+ "kExceptionNames length");
+
+const char kExcPrefix[] = "EXC_";
+const char kExcMaskPrefix[] = "EXC_MASK_";
+
+const char* kBehaviorNames[] = {
+ nullptr,
+
+ // sed -Ene 's/^# define[[:space:]]EXCEPTION_([[:graph:]]+)[[:space:]]+[[:digit:]]{1,2}([[:space:]]|$).*/ "\1",/p'
+ // /usr/include/mach/exception_types.h
+ "DEFAULT",
+ "STATE",
+ "STATE_IDENTITY",
+};
+
+const char kBehaviorPrefix[] = "EXCEPTION_";
+const char kMachExceptionCodesFull[] = "MACH_EXCEPTION_CODES";
+const char kMachExceptionCodesShort[] = "MACH";
+
+const char* kFlavorNames[] = {
+ "THREAD_STATE_FLAVOR_LIST",
+
+#if defined(__i386__) || defined(__x86_64__)
+ // sed -Ene 's/^#define ((x86|THREAD)_[[:graph:]]+)[[:space:]]+[[:digit:]]{1,2}.*$/ "\1",/p'
+ // /usr/include/mach/i386/thread_status.h
+ // and then fix up by adding x86_SAVED_STATE32 and x86_SAVED_STATE64.
+ "x86_THREAD_STATE32",
+ "x86_FLOAT_STATE32",
+ "x86_EXCEPTION_STATE32",
+ "x86_THREAD_STATE64",
+ "x86_FLOAT_STATE64",
+ "x86_EXCEPTION_STATE64",
+ "x86_THREAD_STATE",
+ "x86_FLOAT_STATE",
+ "x86_EXCEPTION_STATE",
+ "x86_DEBUG_STATE32",
+ "x86_DEBUG_STATE64",
+ "x86_DEBUG_STATE",
+ "THREAD_STATE_NONE",
+ "x86_SAVED_STATE32",
+ "x86_SAVED_STATE64",
+ "x86_AVX_STATE32",
+ "x86_AVX_STATE64",
+ "x86_AVX_STATE",
+#elif defined(__ppc__) || defined(__ppc64__)
+ // sed -Ene 's/^#define ((PPC|THREAD)_[[:graph:]]+)[[:space:]]+[[:digit:]]{1,2}.*$/ "\1",/p'
+ // usr/include/mach/ppc/thread_status.h
+ // (Mac OS X 10.6 SDK)
+ "PPC_THREAD_STATE",
+ "PPC_FLOAT_STATE",
+ "PPC_EXCEPTION_STATE",
+ "PPC_VECTOR_STATE",
+ "PPC_THREAD_STATE64",
+ "PPC_EXCEPTION_STATE64",
+ "THREAD_STATE_NONE",
+#elif defined(__arm__) || defined(__arm64__)
+ // sed -Ene 's/^#define ((ARM|THREAD)_[[:graph:]]+)[[:space:]]+[[:digit:]]{1,2}.*$/ "\1",/p'
+ // usr/include/mach/arm/thread_status.h
+ // (iOS 7 SDK)
+ // and then fix up by making the list sparse as appropriate.
+ "ARM_THREAD_STATE",
+ "ARM_VFP_STATE",
+ "ARM_EXCEPTION_STATE",
+ "ARM_DEBUG_STATE",
+ "THREAD_STATE_NONE",
+ "ARM_THREAD_STATE64",
+ "ARM_EXCEPTION_STATE64",
+ nullptr,
+ "ARM_THREAD_STATE32",
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ "ARM_DEBUG_STATE32",
+ "ARM_DEBUG_STATE64",
+ "ARM_NEON_STATE",
+ "ARM_NEON_STATE64",
+#endif
+};
+
+// Certain generic flavors have high constants not contiguous with the flavors
+// above. List them separately alongside their constants.
+const struct {
+ thread_state_flavor_t flavor;
+ const char* name;
+} kGenericFlavorNames[] = {
+ {THREAD_STATE_FLAVOR_LIST_NEW, "THREAD_STATE_FLAVOR_LIST_NEW"},
+ {THREAD_STATE_FLAVOR_LIST_10_9, "THREAD_STATE_FLAVOR_LIST_10_9"},
+};
+
+// Returns the short name for a flavor name, given its full flavor name.
+std::string ThreadStateFlavorFullToShort(const base::StringPiece& flavor) {
+ // For generic flavors like THREAD_STATE_NONE and THREAD_STATE_FLAVOR_LIST_*.
+ const char kThreadState[] = "THREAD_STATE_";
+ size_t prefix_len = strlen(kThreadState);
+ const char* flavor_data = flavor.data();
+ size_t flavor_len = flavor.size();
+ if (flavor_len >= prefix_len &&
+ strncmp(flavor_data, kThreadState, prefix_len) == 0) {
+ return std::string(flavor_data + prefix_len, flavor_len - prefix_len);
+ }
+
+ // For architecture-specific flavors.
+#if defined(__i386__) || defined(__x86_64__)
+ const char kArchPrefix[] = "x86_";
+#elif defined(__ppc__) || defined(__ppc64__)
+ const char kArchPrefix[] = "PPC_";
+#elif defined(__arm__) || defined(__arm64__)
+ const char kArchPrefix[] = "ARM_"
+#endif
+ prefix_len = strlen(kArchPrefix);
+ if (flavor_len >= prefix_len &&
+ strncmp(flavor_data, kArchPrefix, prefix_len) == 0) {
+ // Shorten the suffix by removing _STATE. If the suffix contains a
+ // significant designation like 32 or 64, keep it, so that a full name like
+ // x86_THREAD_STATE64 becomes a short name like THREAD64.
+ const struct {
+ const char* orig;
+ const char* repl;
+ } kStateSuffixes[] = {
+ {"_STATE", ""},
+ {"_STATE32", "32"},
+ {"_STATE64", "64"},
+ };
+ for (size_t suffix_index = 0;
+ suffix_index < arraysize(kStateSuffixes);
+ ++suffix_index) {
+ const char* suffix = kStateSuffixes[suffix_index].orig;
+ size_t suffix_len = strlen(suffix);
+ if (flavor_len >= suffix_len &&
+ strncmp(flavor_data + flavor_len - suffix_len, suffix, suffix_len) ==
+ 0) {
+ std::string s(flavor_data + prefix_len,
+ flavor_len - prefix_len - suffix_len);
+ return s.append(kStateSuffixes[suffix_index].repl);
+ }
+ }
+ }
+
+ return std::string(flavor_data, flavor_len);
+}
+
+} // namespace
+
+namespace crashpad {
+
+std::string ExceptionToString(exception_type_t exception,
+ SymbolicConstantToStringOptions options) {
+ const char* exception_name =
+ implicit_cast<size_t>(exception) < arraysize(kExceptionNames)
+ ? kExceptionNames[exception]
+ : nullptr;
+ if (!exception_name) {
+ if (options & kUnknownIsNumeric) {
+ return base::StringPrintf("%d", exception);
+ }
+ return std::string();
+ }
+
+ if (options & kUseShortName) {
+ return std::string(exception_name);
+ }
+ return base::StringPrintf("%s%s", kExcPrefix, exception_name);
+}
+
+bool StringToException(const base::StringPiece& string,
+ StringToSymbolicConstantOptions options,
+ exception_type_t* exception) {
+ if ((options & kAllowFullName) || (options & kAllowShortName)) {
+ bool can_match_full =
+ (options & kAllowFullName) &&
+ string.substr(0, strlen(kExcPrefix)).compare(kExcPrefix) == 0;
+ base::StringPiece short_string =
+ can_match_full ? string.substr(strlen(kExcPrefix)) : string;
+ for (exception_type_t index = 0;
+ index < implicit_cast<exception_type_t>(arraysize(kExceptionNames));
+ ++index) {
+ const char* exception_name = kExceptionNames[index];
+ if (!exception_name) {
+ continue;
+ }
+ if (can_match_full && short_string.compare(exception_name) == 0) {
+ *exception = index;
+ return true;
+ }
+ if ((options & kAllowShortName) && string.compare(exception_name) == 0) {
+ *exception = index;
+ return true;
+ }
+ }
+ }
+
+ if (options & kAllowNumber) {
+ return StringToNumber(string, reinterpret_cast<unsigned int*>(exception));
+ }
+
+ return false;
+}
+
+std::string ExceptionMaskToString(exception_mask_t exception_mask,
+ SymbolicConstantToStringOptions options) {
+ exception_mask_t local_exception_mask = exception_mask;
+ std::string mask_string;
+ bool has_forbidden_or = false;
+ for (size_t exception = 0;
+ exception < arraysize(kExceptionNames);
+ ++exception) {
+ const char* exception_name = kExceptionNames[exception];
+ exception_mask_t exception_mask_value = 1 << exception;
+ if (exception_name && (local_exception_mask & exception_mask_value)) {
+ if (!mask_string.empty()) {
+ if (!(options & kUseOr)) {
+ has_forbidden_or = true;
+ break;
+ }
+ mask_string.append("|");
+ }
+ if (!(options & kUseShortName)) {
+ mask_string.append(kExcMaskPrefix);
+ }
+ mask_string.append(exception_name);
+ local_exception_mask &= ~exception_mask_value;
+ }
+ }
+
+ if (has_forbidden_or) {
+ local_exception_mask = exception_mask;
+ mask_string.clear();
+ }
+
+ // Deal with any remainder.
+ if (local_exception_mask) {
+ if (!(options & kUnknownIsNumeric)) {
+ return std::string();
+ }
+ if (!mask_string.empty()) {
+ mask_string.append("|");
+ }
+ mask_string.append(base::StringPrintf("%#x", local_exception_mask));
+ }
+
+ return mask_string;
+}
+
+bool StringToExceptionMask(const base::StringPiece& string,
+ StringToSymbolicConstantOptions options,
+ exception_mask_t* exception_mask) {
+ if (options & kAllowOr) {
+ options &= ~kAllowOr;
+ exception_mask_t build_mask = 0;
+ size_t pos = -1;
+ do {
+ ++pos;
+ const char* substring_begin = string.begin() + pos;
+ pos = string.find('|', pos);
+ const char* substring_end = (pos == base::StringPiece::npos)
+ ? string.end()
+ : (string.begin() + pos);
+ base::StringPiece substring = string.substr(
+ substring_begin - string.begin(), substring_end - substring_begin);
+
+ exception_mask_t temp_mask;
+ if (!StringToExceptionMask(substring, options, &temp_mask)) {
+ return false;
+ }
+ build_mask |= temp_mask;
+ } while (pos != base::StringPiece::npos);
+
+ *exception_mask = build_mask;
+ return true;
+ }
+
+ if ((options & kAllowFullName) || (options & kAllowShortName)) {
+ bool can_match_full =
+ (options & kAllowFullName) &&
+ string.substr(0, strlen(kExcMaskPrefix)).compare(kExcMaskPrefix) == 0;
+ base::StringPiece short_string =
+ can_match_full ? string.substr(strlen(kExcMaskPrefix)) : string;
+ for (exception_type_t index = 0;
+ index < implicit_cast<exception_type_t>(arraysize(kExceptionNames));
+ ++index) {
+ const char* exception_name = kExceptionNames[index];
+ if (!exception_name) {
+ continue;
+ }
+ if (can_match_full && short_string.compare(exception_name) == 0) {
+ *exception_mask = 1 << index;
+ return true;
+ }
+ if ((options & kAllowShortName) && string.compare(exception_name) == 0) {
+ *exception_mask = 1 << index;
+ return true;
+ }
+ }
+
+ // EXC_MASK_ALL is a special case: it is not in kExceptionNames as it exists
+ // only as a mask value.
+ const char kExcMaskAll[] = "ALL";
+ if ((can_match_full && short_string.compare(kExcMaskAll) == 0) ||
+ ((options & kAllowShortName) && string.compare(kExcMaskAll) == 0)) {
+ *exception_mask = ExcMaskAll();
+ return true;
+ }
+ }
+
+ if (options & kAllowNumber) {
+ return StringToNumber(string,
+ reinterpret_cast<unsigned int*>(exception_mask));
+ }
+
+ return false;
+}
+
+std::string ExceptionBehaviorToString(exception_behavior_t behavior,
+ SymbolicConstantToStringOptions options) {
+ const exception_behavior_t basic_behavior = ExceptionBehaviorBasic(behavior);
+
+ const char* behavior_name =
+ implicit_cast<size_t>(basic_behavior) < arraysize(kBehaviorNames)
+ ? kBehaviorNames[basic_behavior]
+ : nullptr;
+ if (!behavior_name) {
+ if (options & kUnknownIsNumeric) {
+ return base::StringPrintf("%#x", behavior);
+ }
+ return std::string();
+ }
+
+ std::string behavior_string;
+ if (options & kUseShortName) {
+ behavior_string.assign(behavior_name);
+ } else {
+ behavior_string.assign(base::StringPrintf(
+ "%s%s", kBehaviorPrefix, behavior_name));
+ }
+
+ if (ExceptionBehaviorHasMachExceptionCodes(behavior)) {
+ behavior_string.append("|");
+ behavior_string.append((options & kUseShortName) ? kMachExceptionCodesShort
+ : kMachExceptionCodesFull);
+ }
+
+ return behavior_string;
+}
+
+bool StringToExceptionBehavior(const base::StringPiece& string,
+ StringToSymbolicConstantOptions options,
+ exception_behavior_t* behavior) {
+ base::StringPiece sp = string;
+ exception_behavior_t build_behavior = 0;
+ size_t pos = sp.find('|', 0);
+ if (pos != base::StringPiece::npos) {
+ base::StringPiece left = sp.substr(0, pos);
+ base::StringPiece right = sp.substr(pos + 1, sp.length() - pos - 1);
+ if (options & kAllowFullName) {
+ if (left.compare(kMachExceptionCodesFull) == 0) {
+ build_behavior |= MACH_EXCEPTION_CODES;
+ sp = right;
+ } else if (right.compare(kMachExceptionCodesFull) == 0) {
+ build_behavior |= MACH_EXCEPTION_CODES;
+ sp = left;
+ }
+ }
+ if (!(build_behavior & MACH_EXCEPTION_CODES) &&
+ (options & kAllowShortName)) {
+ if (left.compare(kMachExceptionCodesShort) == 0) {
+ build_behavior |= MACH_EXCEPTION_CODES;
+ sp = right;
+ } else if (right.compare(kMachExceptionCodesShort) == 0) {
+ build_behavior |= MACH_EXCEPTION_CODES;
+ sp = left;
+ }
+ }
+ if (!(build_behavior & MACH_EXCEPTION_CODES)) {
+ return false;
+ }
+ }
+
+ if ((options & kAllowFullName) || (options & kAllowShortName)) {
+ bool can_match_full =
+ (options & kAllowFullName) &&
+ sp.substr(0, strlen(kBehaviorPrefix)).compare(kBehaviorPrefix) == 0;
+ base::StringPiece short_string =
+ can_match_full ? sp.substr(strlen(kBehaviorPrefix)) : sp;
+ for (exception_behavior_t index = 0;
+ index < implicit_cast<exception_behavior_t>(arraysize(kBehaviorNames));
+ ++index) {
+ const char* behavior_name = kBehaviorNames[index];
+ if (!behavior_name) {
+ continue;
+ }
+ if (can_match_full && short_string.compare(behavior_name) == 0) {
+ build_behavior |= index;
+ *behavior = build_behavior;
+ return true;
+ }
+ if ((options & kAllowShortName) && sp.compare(behavior_name) == 0) {
+ build_behavior |= index;
+ *behavior = build_behavior;
+ return true;
+ }
+ }
+ }
+
+ if (options & kAllowNumber) {
+ exception_behavior_t temp_behavior;
+ if (!StringToNumber(sp, reinterpret_cast<unsigned int*>(&temp_behavior))) {
+ return false;
+ }
+ build_behavior |= temp_behavior;
+ *behavior = build_behavior;
+ return true;
+ }
+
+ return false;
+}
+
+std::string ThreadStateFlavorToString(thread_state_flavor_t flavor,
+ SymbolicConstantToStringOptions options) {
+ const char* flavor_name =
+ implicit_cast<size_t>(flavor) < arraysize(kFlavorNames)
+ ? kFlavorNames[flavor]
+ : nullptr;
+
+ if (!flavor_name) {
+ for (size_t generic_flavor_index = 0;
+ generic_flavor_index < arraysize(kGenericFlavorNames);
+ ++generic_flavor_index) {
+ if (flavor == kGenericFlavorNames[generic_flavor_index].flavor) {
+ flavor_name = kGenericFlavorNames[generic_flavor_index].name;
+ break;
+ }
+ }
+ }
+
+ if (!flavor_name) {
+ if (options & kUnknownIsNumeric) {
+ return base::StringPrintf("%d", flavor);
+ }
+ return std::string();
+ }
+
+ if (options & kUseShortName) {
+ return ThreadStateFlavorFullToShort(flavor_name);
+ }
+ return std::string(flavor_name);
+}
+
+bool StringToThreadStateFlavor(const base::StringPiece& string,
+ StringToSymbolicConstantOptions options,
+ thread_state_flavor_t* flavor) {
+ if ((options & kAllowFullName) || (options & kAllowShortName)) {
+ for (thread_state_flavor_t index = 0;
+ index < implicit_cast<thread_state_flavor_t>(arraysize(kFlavorNames));
+ ++index) {
+ const char* flavor_name = kFlavorNames[index];
+ if (!flavor_name) {
+ continue;
+ }
+ if ((options & kAllowFullName) && string.compare(flavor_name) == 0) {
+ *flavor = index;
+ return true;
+ }
+ if (options & kAllowShortName) {
+ std::string short_name = ThreadStateFlavorFullToShort(flavor_name);
+ if (string.compare(short_name) == 0) {
+ *flavor = index;
+ return true;
+ }
+ }
+ }
+
+ for (size_t generic_flavor_index = 0;
+ generic_flavor_index < arraysize(kGenericFlavorNames);
+ ++generic_flavor_index) {
+ const char* flavor_name = kGenericFlavorNames[generic_flavor_index].name;
+ thread_state_flavor_t flavor_number =
+ kGenericFlavorNames[generic_flavor_index].flavor;
+ if ((options & kAllowFullName) && string.compare(flavor_name) == 0) {
+ *flavor = flavor_number;
+ return true;
+ }
+ if (options & kAllowShortName) {
+ std::string short_name = ThreadStateFlavorFullToShort(flavor_name);
+ if (string.compare(short_name) == 0) {
+ *flavor = flavor_number;
+ return true;
+ }
+ }
+ }
+ }
+
+ if (options & kAllowNumber) {
+ return StringToNumber(string, reinterpret_cast<unsigned int*>(flavor));
+ }
+
+ return false;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach.h b/chromium/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach.h
new file mode 100644
index 00000000000..be522fa8758
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach.h
@@ -0,0 +1,120 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MACH_SYMBOLIC_CONSTANTS_MACH_H_
+#define CRASHPAD_UTIL_MACH_SYMBOLIC_CONSTANTS_MACH_H_
+
+#include <mach/mach.h>
+
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "util/misc/symbolic_constants_common.h"
+
+namespace crashpad {
+
+//! \brief Converts a Mach exception value to a textual representation.
+//!
+//! \param[in] exception The Mach exception value to convert.
+//! \param[in] options Options affecting the conversion. ::kUseOr is ignored.
+//! For ::kUnknownIsNumeric, the format is `"%d"`.
+//!
+//! \return The converted string.
+std::string ExceptionToString(exception_type_t exception,
+ SymbolicConstantToStringOptions options);
+
+//! \brief Converts a string to its corresponding Mach exception value.
+//!
+//! \param[in] string The string to convert.
+//! \param[in] options Options affecting the conversion. ::kAllowOr is ignored.
+//! \param[out] exception The converted Mach exception value.
+//!
+//! \return `true` on success, `false` if \a string could not be converted as
+//! requested.
+bool StringToException(const base::StringPiece& string,
+ StringToSymbolicConstantOptions options,
+ exception_type_t* exception);
+
+//! \brief Converts a Mach exception mask value to a textual representation.
+//!
+//! \param[in] exception_mask The Mach exception mask value to convert.
+//! \param[in] options Options affecting the conversion. ::kUseOr is honored.
+//! For ::kUnknownIsNumeric, the format is `"%#x"`.
+//!
+//! \return The converted string.
+std::string ExceptionMaskToString(exception_mask_t exception_mask,
+ SymbolicConstantToStringOptions options);
+
+//! \brief Converts a string to its corresponding Mach exception mask value.
+//!
+//! \param[in] string The string to convert.
+//! \param[in] options Options affecting the conversion. ::kAllowOr is honored.
+//! \param[out] exception_mask The converted Mach exception mask value.
+//!
+//! \return `true` on success, `false` if \a string could not be converted as
+//! requested.
+bool StringToExceptionMask(const base::StringPiece& string,
+ StringToSymbolicConstantOptions options,
+ exception_mask_t* exception_mask);
+
+//! \brief Converts a Mach exception behavior value to a textual representation.
+//!
+//! \param[in] behavior The Mach exception behavior value to convert.
+//! \param[in] options Options affecting the conversion. ::kUseOr is ignored.
+//! `MACH_EXCEPTION_CODES` can always be ORed in, but no other values can be
+//! ORed with each other. For ::kUnknownIsNumeric, the format is `"%#x"`.
+//!
+//! \return The converted string.
+std::string ExceptionBehaviorToString(exception_behavior_t behavior,
+ SymbolicConstantToStringOptions options);
+
+//! \brief Converts a string to its corresponding Mach exception behavior value.
+//!
+//! \param[in] string The string to convert.
+//! \param[in] options Options affecting the conversion. ::kAllowOr is ignored.
+//! `MACH_EXCEPTION_CODES` can always be ORed in, but no other values can be
+//! ORed with each other.
+//! \param[out] behavior The converted Mach exception behavior value.
+//!
+//! \return `true` on success, `false` if \a string could not be converted as
+//! requested.
+bool StringToExceptionBehavior(const base::StringPiece& string,
+ StringToSymbolicConstantOptions options,
+ exception_behavior_t* behavior);
+
+//! \brief Converts a thread state flavor value to a textual representation.
+//!
+//! \param[in] flavor The thread state flavor value to convert.
+//! \param[in] options Options affecting the conversion. ::kUseOr is ignored.
+//! For ::kUnknownIsNumeric, the format is `"%d"`.
+//!
+//! \return The converted string.
+std::string ThreadStateFlavorToString(thread_state_flavor_t flavor,
+ SymbolicConstantToStringOptions options);
+
+//! \brief Converts a string to its corresponding thread state flavor value.
+//!
+//! \param[in] string The string to convert.
+//! \param[in] options Options affecting the conversion. ::kAllowOr is ignored.
+//! \param[out] flavor The converted thread state flavor value.
+//!
+//! \return `true` on success, `false` if \a string could not be converted as
+//! requested.
+bool StringToThreadStateFlavor(const base::StringPiece& string,
+ StringToSymbolicConstantOptions options,
+ thread_state_flavor_t* flavor);
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MACH_SYMBOLIC_CONSTANTS_MACH_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach_test.cc b/chromium/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach_test.cc
new file mode 100644
index 00000000000..6878cbf8ebd
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach_test.cc
@@ -0,0 +1,1066 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/symbolic_constants_mach.h"
+
+#include <mach/mach.h>
+#include <string.h>
+
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+#include "util/mach/mach_extensions.h"
+
+#define NUL_TEST_DATA(string) { string, arraysize(string) - 1 }
+
+namespace crashpad {
+namespace test {
+namespace {
+
+// Options to use for normal tests, those that don’t require kAllowOr.
+const StringToSymbolicConstantOptions kNormalOptions[] = {
+ 0,
+ kAllowFullName,
+ kAllowShortName,
+ kAllowFullName | kAllowShortName,
+ kAllowNumber,
+ kAllowFullName | kAllowNumber,
+ kAllowShortName | kAllowNumber,
+ kAllowFullName | kAllowShortName | kAllowNumber,
+};
+
+// If |expect| is nullptr, the conversion is expected to fail. If |expect| is
+// empty, the conversion is expected to succeed, but the precise returned string
+// value is unknown. Otherwise, the conversion is expected to succeed, and
+// |expect| contains the precise expected string value to be returned. If
+// |expect| contains the substring "0x1", the conversion is expected only to
+// succeed when kUnknownIsNumeric is set.
+//
+// Only set kUseFullName or kUseShortName when calling this. Other options are
+// exercised directly by this function.
+template <typename Traits>
+void TestSomethingToStringOnce(typename Traits::ValueType value,
+ const char* expect,
+ SymbolicConstantToStringOptions options) {
+ std::string actual =
+ Traits::SomethingToString(value, options | kUnknownIsEmpty | kUseOr);
+ std::string actual_numeric =
+ Traits::SomethingToString(value, options | kUnknownIsNumeric | kUseOr);
+ if (expect) {
+ if (expect[0] == '\0') {
+ EXPECT_FALSE(actual.empty()) << Traits::kValueName << " " << value;
+ } else if (strstr(expect, "0x1")) {
+ EXPECT_TRUE(actual.empty()) << Traits::kValueName << " " << value
+ << ", actual " << actual;
+ actual.assign(expect);
+ } else {
+ EXPECT_EQ(expect, actual) << Traits::kValueName << " " << value;
+ }
+ EXPECT_EQ(actual, actual_numeric) << Traits::kValueName << " " << value;
+ } else {
+ EXPECT_TRUE(actual.empty()) << Traits::kValueName << " " << value
+ << ", actual " << actual;
+ EXPECT_FALSE(actual_numeric.empty()) << Traits::kValueName << " " << value
+ << ", actual_numeric "
+ << actual_numeric;
+ }
+}
+
+template <typename Traits>
+void TestSomethingToString(typename Traits::ValueType value,
+ const char* expect_full,
+ const char* expect_short) {
+ {
+ SCOPED_TRACE("full_name");
+ TestSomethingToStringOnce<Traits>(value, expect_full, kUseFullName);
+ }
+
+ {
+ SCOPED_TRACE("short_name");
+ TestSomethingToStringOnce<Traits>(value, expect_short, kUseShortName);
+ }
+}
+
+template <typename Traits>
+void TestStringToSomething(const base::StringPiece& string,
+ StringToSymbolicConstantOptions options,
+ bool expect_result,
+ typename Traits::ValueType expect_value) {
+ typename Traits::ValueType actual_value;
+ bool actual_result =
+ Traits::StringToSomething(string, options, &actual_value);
+ if (expect_result) {
+ EXPECT_TRUE(actual_result) << "string " << string << ", options " << options
+ << ", " << Traits::kValueName << " "
+ << expect_value;
+ if (actual_result) {
+ EXPECT_EQ(expect_value, actual_value) << "string " << string
+ << ", options " << options;
+ }
+ } else {
+ EXPECT_FALSE(actual_result) << "string " << string << ", options "
+ << options << ", " << Traits::kValueName << " "
+ << actual_value;
+ }
+}
+
+const struct {
+ exception_type_t exception;
+ const char* full_name;
+ const char* short_name;
+} kExceptionTestData[] = {
+ {EXC_BAD_ACCESS, "EXC_BAD_ACCESS", "BAD_ACCESS"},
+ {EXC_BAD_INSTRUCTION, "EXC_BAD_INSTRUCTION", "BAD_INSTRUCTION"},
+ {EXC_ARITHMETIC, "EXC_ARITHMETIC", "ARITHMETIC"},
+ {EXC_EMULATION, "EXC_EMULATION", "EMULATION"},
+ {EXC_SOFTWARE, "EXC_SOFTWARE", "SOFTWARE"},
+ {EXC_MACH_SYSCALL, "EXC_MACH_SYSCALL", "MACH_SYSCALL"},
+ {EXC_RPC_ALERT, "EXC_RPC_ALERT", "RPC_ALERT"},
+ {EXC_CRASH, "EXC_CRASH", "CRASH"},
+ {EXC_RESOURCE, "EXC_RESOURCE", "RESOURCE"},
+ {EXC_GUARD, "EXC_GUARD", "GUARD"},
+};
+
+struct ConvertExceptionTraits {
+ using ValueType = exception_type_t;
+ static std::string SomethingToString(
+ ValueType value,
+ SymbolicConstantToStringOptions options) {
+ return ExceptionToString(value, options);
+ }
+ static bool StringToSomething(const base::StringPiece& string,
+ StringToSymbolicConstantOptions options,
+ ValueType* value) {
+ return StringToException(string, options, value);
+ }
+ static const char kValueName[];
+};
+const char ConvertExceptionTraits::kValueName[] = "exception";
+
+void TestExceptionToString(exception_type_t value,
+ const char* expect_full,
+ const char* expect_short) {
+ return TestSomethingToString<ConvertExceptionTraits>(
+ value, expect_full, expect_short);
+}
+
+TEST(SymbolicConstantsMach, ExceptionToString) {
+ for (size_t index = 0; index < arraysize(kExceptionTestData); ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %zu", index));
+ TestExceptionToString(kExceptionTestData[index].exception,
+ kExceptionTestData[index].full_name,
+ kExceptionTestData[index].short_name);
+ }
+
+ for (exception_type_t exception = 0;
+ exception < EXC_TYPES_COUNT + 8;
+ ++exception) {
+ SCOPED_TRACE(base::StringPrintf("exception %d", exception));
+ if (exception > 0 && exception < EXC_TYPES_COUNT) {
+ TestExceptionToString(exception, "", "");
+ } else {
+ TestExceptionToString(exception, nullptr, nullptr);
+ }
+ }
+}
+
+void TestStringToException(const base::StringPiece& string,
+ StringToSymbolicConstantOptions options,
+ bool expect_result,
+ exception_type_t expect_value) {
+ return TestStringToSomething<ConvertExceptionTraits>(
+ string, options, expect_result, expect_value);
+}
+
+TEST(SymbolicConstantsMach, StringToException) {
+ for (size_t option_index = 0;
+ option_index < arraysize(kNormalOptions);
+ ++option_index) {
+ SCOPED_TRACE(base::StringPrintf("option_index %zu", option_index));
+ StringToSymbolicConstantOptions options = kNormalOptions[option_index];
+ for (size_t index = 0; index < arraysize(kExceptionTestData); ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %zu", index));
+ exception_type_t exception = kExceptionTestData[index].exception;
+ {
+ SCOPED_TRACE("full_name");
+ TestStringToException(kExceptionTestData[index].full_name,
+ options,
+ options & kAllowFullName,
+ exception);
+ }
+ {
+ SCOPED_TRACE("short_name");
+ TestStringToException(kExceptionTestData[index].short_name,
+ options,
+ options & kAllowShortName,
+ exception);
+ }
+ {
+ SCOPED_TRACE("number");
+ std::string number_string = base::StringPrintf("%d", exception);
+ TestStringToException(
+ number_string, options, options & kAllowNumber, exception);
+ }
+ }
+
+ const char* const kNegativeTestData[] = {
+ "EXC_CRASH ",
+ " EXC_BAD_INSTRUCTION",
+ "CRASH ",
+ " BAD_INSTRUCTION",
+ "EXC_EXC_BAD_ACCESS",
+ "EXC_SOFTWARES",
+ "SOFTWARES",
+ "EXC_JUNK",
+ "random",
+ "",
+ };
+
+ for (size_t index = 0; index < arraysize(kNegativeTestData); ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %zu", index));
+ TestStringToException(kNegativeTestData[index], options, false, 0);
+ }
+
+ const struct {
+ const char* string;
+ size_t length;
+ } kNULTestData[] = {
+ NUL_TEST_DATA("\0EXC_ARITHMETIC"),
+ NUL_TEST_DATA("EXC_\0ARITHMETIC"),
+ NUL_TEST_DATA("EXC_ARITH\0METIC"),
+ NUL_TEST_DATA("EXC_ARITHMETIC\0"),
+ NUL_TEST_DATA("\0ARITHMETIC"),
+ NUL_TEST_DATA("ARITH\0METIC"),
+ NUL_TEST_DATA("ARITHMETIC\0"),
+ NUL_TEST_DATA("\0003"),
+ NUL_TEST_DATA("3\0"),
+ NUL_TEST_DATA("1\0002"),
+ };
+
+ for (size_t index = 0; index < arraysize(kNULTestData); ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %zu", index));
+ base::StringPiece string(kNULTestData[index].string,
+ kNULTestData[index].length);
+ TestStringToException(string, options, false, 0);
+ }
+ }
+
+ // Ensure that a NUL is not required at the end of the string.
+ {
+ SCOPED_TRACE("trailing_NUL_full");
+ TestStringToException(base::StringPiece("EXC_BREAKPOINTED", 14),
+ kAllowFullName,
+ true,
+ EXC_BREAKPOINT);
+ }
+ {
+ SCOPED_TRACE("trailing_NUL_short");
+ TestStringToException(base::StringPiece("BREAKPOINTED", 10),
+ kAllowShortName,
+ true,
+ EXC_BREAKPOINT);
+ }
+}
+
+const struct {
+ exception_mask_t exception_mask;
+ const char* full_name;
+ const char* short_name;
+} kExceptionMaskTestData[] = {
+ {EXC_MASK_BAD_ACCESS, "EXC_MASK_BAD_ACCESS", "BAD_ACCESS"},
+ {EXC_MASK_BAD_INSTRUCTION, "EXC_MASK_BAD_INSTRUCTION", "BAD_INSTRUCTION"},
+ {EXC_MASK_ARITHMETIC, "EXC_MASK_ARITHMETIC", "ARITHMETIC"},
+ {EXC_MASK_EMULATION, "EXC_MASK_EMULATION", "EMULATION"},
+ {EXC_MASK_SOFTWARE, "EXC_MASK_SOFTWARE", "SOFTWARE"},
+ {EXC_MASK_MACH_SYSCALL, "EXC_MASK_MACH_SYSCALL", "MACH_SYSCALL"},
+ {EXC_MASK_RPC_ALERT, "EXC_MASK_RPC_ALERT", "RPC_ALERT"},
+ {EXC_MASK_CRASH, "EXC_MASK_CRASH", "CRASH"},
+ {EXC_MASK_RESOURCE, "EXC_MASK_RESOURCE", "RESOURCE"},
+ {EXC_MASK_GUARD, "EXC_MASK_GUARD", "GUARD"},
+ {0x1, "0x1", "0x1"},
+ {EXC_MASK_CRASH | 0x1, "EXC_MASK_CRASH|0x1", "CRASH|0x1"},
+ {EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC |
+ EXC_MASK_EMULATION |
+ EXC_MASK_SOFTWARE |
+ EXC_MASK_BREAKPOINT |
+ EXC_MASK_SYSCALL |
+ EXC_MASK_MACH_SYSCALL |
+ EXC_MASK_RPC_ALERT,
+ "EXC_MASK_BAD_ACCESS|EXC_MASK_BAD_INSTRUCTION|EXC_MASK_ARITHMETIC|"
+ "EXC_MASK_EMULATION|EXC_MASK_SOFTWARE|EXC_MASK_BREAKPOINT|"
+ "EXC_MASK_SYSCALL|EXC_MASK_MACH_SYSCALL|EXC_MASK_RPC_ALERT",
+ "BAD_ACCESS|BAD_INSTRUCTION|ARITHMETIC|EMULATION|SOFTWARE|BREAKPOINT|"
+ "SYSCALL|MACH_SYSCALL|RPC_ALERT"},
+ {EXC_MASK_RESOURCE | EXC_MASK_GUARD,
+ "EXC_MASK_RESOURCE|EXC_MASK_GUARD",
+ "RESOURCE|GUARD"},
+};
+
+struct ConvertExceptionMaskTraits {
+ using ValueType = exception_mask_t;
+ static std::string SomethingToString(
+ ValueType value,
+ SymbolicConstantToStringOptions options) {
+ return ExceptionMaskToString(value, options);
+ }
+ static bool StringToSomething(const base::StringPiece& string,
+ StringToSymbolicConstantOptions options,
+ ValueType* value) {
+ return StringToExceptionMask(string, options, value);
+ }
+ static const char kValueName[];
+};
+const char ConvertExceptionMaskTraits::kValueName[] = "exception_mask";
+
+void TestExceptionMaskToString(exception_mask_t value,
+ const char* expect_full,
+ const char* expect_short) {
+ return TestSomethingToString<ConvertExceptionMaskTraits>(
+ value, expect_full, expect_short);
+}
+
+TEST(SymbolicConstantsMach, ExceptionMaskToString) {
+ for (size_t index = 0; index < arraysize(kExceptionMaskTestData); ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %zu", index));
+ TestExceptionMaskToString(kExceptionMaskTestData[index].exception_mask,
+ kExceptionMaskTestData[index].full_name,
+ kExceptionMaskTestData[index].short_name);
+ }
+
+ // Test kUseOr handling.
+ EXPECT_TRUE(ExceptionMaskToString(EXC_MASK_CRASH | EXC_MASK_GUARD,
+ kUseFullName).empty());
+ EXPECT_TRUE(ExceptionMaskToString(EXC_MASK_CRASH | EXC_MASK_GUARD,
+ kUseShortName).empty());
+ EXPECT_EQ("0x1400",
+ ExceptionMaskToString(EXC_MASK_CRASH | EXC_MASK_GUARD,
+ kUseFullName | kUnknownIsNumeric));
+ EXPECT_EQ("0x1400",
+ ExceptionMaskToString(EXC_MASK_CRASH | EXC_MASK_GUARD,
+ kUseShortName | kUnknownIsNumeric));
+ EXPECT_EQ("EXC_MASK_CRASH|EXC_MASK_GUARD",
+ ExceptionMaskToString(EXC_MASK_CRASH | EXC_MASK_GUARD,
+ kUseFullName | kUseOr));
+ EXPECT_EQ("CRASH|GUARD",
+ ExceptionMaskToString(EXC_MASK_CRASH | EXC_MASK_GUARD,
+ kUseShortName | kUseOr));
+}
+
+void TestStringToExceptionMask(const base::StringPiece& string,
+ StringToSymbolicConstantOptions options,
+ bool expect_result,
+ exception_mask_t expect_value) {
+ return TestStringToSomething<ConvertExceptionMaskTraits>(
+ string, options, expect_result, expect_value);
+}
+
+TEST(SymbolicConstantsMach, StringToExceptionMask) {
+ // Don’t use kNormalOptions, because kAllowOr needs to be tested.
+ const StringToSymbolicConstantOptions kOptions[] = {
+ 0,
+ kAllowFullName,
+ kAllowShortName,
+ kAllowFullName | kAllowShortName,
+ kAllowNumber,
+ kAllowFullName | kAllowNumber,
+ kAllowShortName | kAllowNumber,
+ kAllowFullName | kAllowShortName | kAllowNumber,
+ kAllowOr,
+ kAllowFullName | kAllowOr,
+ kAllowShortName | kAllowOr,
+ kAllowFullName | kAllowShortName | kAllowOr,
+ kAllowNumber | kAllowOr,
+ kAllowFullName | kAllowNumber | kAllowOr,
+ kAllowShortName | kAllowNumber | kAllowOr,
+ kAllowFullName | kAllowShortName | kAllowNumber | kAllowOr,
+ };
+
+ for (size_t option_index = 0;
+ option_index < arraysize(kOptions);
+ ++option_index) {
+ SCOPED_TRACE(base::StringPrintf("option_index %zu", option_index));
+ StringToSymbolicConstantOptions options = kOptions[option_index];
+ for (size_t index = 0; index < arraysize(kExceptionMaskTestData); ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %zu", index));
+ exception_mask_t exception_mask =
+ kExceptionMaskTestData[index].exception_mask;
+ {
+ SCOPED_TRACE("full_name");
+ base::StringPiece full_name(kExceptionMaskTestData[index].full_name);
+ bool has_number = full_name.find("0x", 0) != base::StringPiece::npos;
+ bool has_or = full_name.find('|', 0) != base::StringPiece::npos;
+ bool allowed_characteristics =
+ (has_number ? (options & kAllowNumber) : true) &&
+ (has_or ? (options & kAllowOr) : true);
+ bool is_number = full_name.compare("0x1") == 0;
+ bool expect_valid =
+ ((options & kAllowFullName) && allowed_characteristics) ||
+ ((options & kAllowNumber) && is_number);
+ TestStringToExceptionMask(
+ full_name, options, expect_valid, exception_mask);
+ }
+ {
+ SCOPED_TRACE("short_name");
+ base::StringPiece short_name(kExceptionMaskTestData[index].short_name);
+ bool has_number = short_name.find("0x", 0) != base::StringPiece::npos;
+ bool has_or = short_name.find('|', 0) != base::StringPiece::npos;
+ bool allowed_characteristics =
+ (has_number ? (options & kAllowNumber) : true) &&
+ (has_or ? (options & kAllowOr) : true);
+ bool is_number = short_name.compare("0x1") == 0;
+ bool expect_valid =
+ ((options & kAllowShortName) && allowed_characteristics) ||
+ ((options & kAllowNumber) && is_number);
+ TestStringToExceptionMask(
+ short_name, options, expect_valid, exception_mask);
+ }
+ }
+
+ const char* const kNegativeTestData[] = {
+ "EXC_MASK_CRASH ",
+ " EXC_MASK_BAD_INSTRUCTION",
+ "EXC_MASK_EXC_MASK_BAD_ACCESS",
+ "EXC_MASK_SOFTWARES",
+ "EXC_MASK_JUNK",
+ "EXC_GUARD",
+ "EXC_ARITHMETIC|EXC_FAKE",
+ "ARITHMETIC|FAKE",
+ "FAKE|ARITHMETIC",
+ "EXC_FAKE|EXC_ARITHMETIC",
+ "random",
+ "",
+ };
+
+ for (size_t index = 0; index < arraysize(kNegativeTestData); ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %zu", index));
+ TestStringToExceptionMask(kNegativeTestData[index], options, false, 0);
+ }
+
+ const struct {
+ const char* string;
+ size_t length;
+ } kNULTestData[] = {
+ NUL_TEST_DATA("\0EXC_MASK_ARITHMETIC"),
+ NUL_TEST_DATA("EXC_\0MASK_ARITHMETIC"),
+ NUL_TEST_DATA("EXC_MASK_\0ARITHMETIC"),
+ NUL_TEST_DATA("EXC_MASK_ARITH\0METIC"),
+ NUL_TEST_DATA("EXC_MASK_ARITHMETIC\0"),
+ NUL_TEST_DATA("\0ARITHMETIC"),
+ NUL_TEST_DATA("ARITH\0METIC"),
+ NUL_TEST_DATA("ARITHMETIC\0"),
+ NUL_TEST_DATA("\0003"),
+ NUL_TEST_DATA("3\0"),
+ NUL_TEST_DATA("1\0002"),
+ NUL_TEST_DATA("EXC_MASK_ARITHMETIC\0|EXC_MASK_EMULATION"),
+ NUL_TEST_DATA("EXC_MASK_ARITHMETIC|\0EXC_MASK_EMULATION"),
+ NUL_TEST_DATA("ARITHMETIC\0|EMULATION"),
+ NUL_TEST_DATA("ARITHMETIC|\0EMULATION"),
+ };
+
+ for (size_t index = 0; index < arraysize(kNULTestData); ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %zu", index));
+ base::StringPiece string(kNULTestData[index].string,
+ kNULTestData[index].length);
+ TestStringToExceptionMask(string, options, false, 0);
+ }
+ }
+
+ const struct {
+ const char* string;
+ StringToSymbolicConstantOptions options;
+ exception_mask_t mask;
+ } kNonCanonicalTestData[] = {
+ {"EXC_MASK_ALL", kAllowFullName, ExcMaskAll()},
+ {"ALL", kAllowShortName, ExcMaskAll()},
+ {"EXC_MASK_ALL|EXC_MASK_CRASH",
+ kAllowFullName | kAllowOr,
+ ExcMaskAll() | EXC_MASK_CRASH},
+ {"ALL|CRASH",
+ kAllowShortName | kAllowOr,
+ ExcMaskAll() | EXC_MASK_CRASH},
+ {"EXC_MASK_BAD_INSTRUCTION|EXC_MASK_BAD_ACCESS",
+ kAllowFullName | kAllowOr,
+ EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION},
+ {"EMULATION|ARITHMETIC",
+ kAllowShortName | kAllowOr,
+ EXC_MASK_ARITHMETIC | EXC_MASK_EMULATION},
+ {"EXC_MASK_SOFTWARE|BREAKPOINT",
+ kAllowFullName | kAllowShortName | kAllowOr,
+ EXC_MASK_SOFTWARE | EXC_MASK_BREAKPOINT},
+ {"SYSCALL|0x100",
+ kAllowShortName | kAllowNumber | kAllowOr,
+ EXC_MASK_SYSCALL | 0x100},
+ };
+
+ for (size_t index = 0; index < arraysize(kNonCanonicalTestData); ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %zu", index));
+ TestStringToExceptionMask(kNonCanonicalTestData[index].string,
+ kNonCanonicalTestData[index].options,
+ true,
+ kNonCanonicalTestData[index].mask);
+ }
+
+ // Ensure that a NUL is not required at the end of the string.
+ {
+ SCOPED_TRACE("trailing_NUL_full");
+ TestStringToExceptionMask(base::StringPiece("EXC_MASK_BREAKPOINTED", 19),
+ kAllowFullName,
+ true,
+ EXC_MASK_BREAKPOINT);
+ }
+ {
+ SCOPED_TRACE("trailing_NUL_short");
+ TestStringToExceptionMask(base::StringPiece("BREAKPOINTED", 10),
+ kAllowShortName,
+ true,
+ EXC_MASK_BREAKPOINT);
+ }
+}
+
+const struct {
+ exception_behavior_t behavior;
+ const char* full_name;
+ const char* short_name;
+} kExceptionBehaviorTestData[] = {
+ {EXCEPTION_DEFAULT, "EXCEPTION_DEFAULT", "DEFAULT"},
+ {EXCEPTION_STATE, "EXCEPTION_STATE", "STATE"},
+ {EXCEPTION_STATE_IDENTITY, "EXCEPTION_STATE_IDENTITY", "STATE_IDENTITY"},
+ {implicit_cast<exception_behavior_t>(EXCEPTION_DEFAULT |
+ MACH_EXCEPTION_CODES),
+ "EXCEPTION_DEFAULT|MACH_EXCEPTION_CODES",
+ "DEFAULT|MACH"},
+ {implicit_cast<exception_behavior_t>(EXCEPTION_STATE |
+ MACH_EXCEPTION_CODES),
+ "EXCEPTION_STATE|MACH_EXCEPTION_CODES",
+ "STATE|MACH"},
+ {implicit_cast<exception_behavior_t>(EXCEPTION_STATE_IDENTITY |
+ MACH_EXCEPTION_CODES),
+ "EXCEPTION_STATE_IDENTITY|MACH_EXCEPTION_CODES",
+ "STATE_IDENTITY|MACH"},
+};
+
+struct ConvertExceptionBehaviorTraits {
+ using ValueType = exception_behavior_t;
+ static std::string SomethingToString(
+ ValueType value,
+ SymbolicConstantToStringOptions options) {
+ return ExceptionBehaviorToString(value, options);
+ }
+ static bool StringToSomething(const base::StringPiece& string,
+ StringToSymbolicConstantOptions options,
+ ValueType* value) {
+ return StringToExceptionBehavior(string, options, value);
+ }
+ static const char kValueName[];
+};
+const char ConvertExceptionBehaviorTraits::kValueName[] = "behavior";
+
+void TestExceptionBehaviorToString(exception_behavior_t value,
+ const char* expect_full,
+ const char* expect_short) {
+ return TestSomethingToString<ConvertExceptionBehaviorTraits>(
+ value, expect_full, expect_short);
+}
+
+TEST(SymbolicConstantsMach, ExceptionBehaviorToString) {
+ for (size_t index = 0;
+ index < arraysize(kExceptionBehaviorTestData);
+ ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %zu", index));
+ TestExceptionBehaviorToString(kExceptionBehaviorTestData[index].behavior,
+ kExceptionBehaviorTestData[index].full_name,
+ kExceptionBehaviorTestData[index].short_name);
+ }
+
+ for (exception_behavior_t behavior = 0; behavior < 8; ++behavior) {
+ SCOPED_TRACE(base::StringPrintf("behavior %d", behavior));
+ exception_behavior_t behavior_mach = behavior | MACH_EXCEPTION_CODES;
+ if (behavior > 0 && behavior <= EXCEPTION_STATE_IDENTITY) {
+ TestExceptionBehaviorToString(behavior, "", "");
+ TestExceptionBehaviorToString(behavior_mach, "", "");
+ } else {
+ TestExceptionBehaviorToString(behavior, nullptr, nullptr);
+ TestExceptionBehaviorToString(behavior_mach, nullptr, nullptr);
+ }
+ }
+}
+
+void TestStringToExceptionBehavior(const base::StringPiece& string,
+ StringToSymbolicConstantOptions options,
+ bool expect_result,
+ exception_behavior_t expect_value) {
+ return TestStringToSomething<ConvertExceptionBehaviorTraits>(
+ string, options, expect_result, expect_value);
+}
+
+TEST(SymbolicConstantsMach, StringToExceptionBehavior) {
+ for (size_t option_index = 0;
+ option_index < arraysize(kNormalOptions);
+ ++option_index) {
+ SCOPED_TRACE(base::StringPrintf("option_index %zu", option_index));
+ StringToSymbolicConstantOptions options = kNormalOptions[option_index];
+ for (size_t index = 0;
+ index < arraysize(kExceptionBehaviorTestData);
+ ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %zu", index));
+ exception_behavior_t behavior =
+ kExceptionBehaviorTestData[index].behavior;
+ {
+ SCOPED_TRACE("full_name");
+ TestStringToExceptionBehavior(
+ kExceptionBehaviorTestData[index].full_name,
+ options,
+ options & kAllowFullName,
+ behavior);
+ }
+ {
+ SCOPED_TRACE("short_name");
+ TestStringToExceptionBehavior(
+ kExceptionBehaviorTestData[index].short_name,
+ options,
+ options & kAllowShortName,
+ behavior);
+ }
+ {
+ SCOPED_TRACE("number");
+ std::string number_string = base::StringPrintf("0x%x", behavior);
+ TestStringToExceptionBehavior(
+ number_string, options, options & kAllowNumber, behavior);
+ }
+ }
+
+ const char* const kNegativeTestData[] = {
+ "EXCEPTION_DEFAULT ",
+ " EXCEPTION_STATE",
+ "EXCEPTION_EXCEPTION_STATE_IDENTITY",
+ "EXCEPTION_DEFAULTS",
+ "EXCEPTION_JUNK",
+ "random",
+ "MACH_EXCEPTION_CODES",
+ "MACH",
+ "MACH_EXCEPTION_CODES|MACH_EXCEPTION_CODES",
+ "MACH_EXCEPTION_CODES|EXCEPTION_NONEXISTENT",
+ "MACH|junk",
+ "EXCEPTION_DEFAULT|EXCEPTION_STATE",
+ "1|2",
+ "",
+ };
+
+ for (size_t index = 0; index < arraysize(kNegativeTestData); ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %zu", index));
+ TestStringToExceptionBehavior(
+ kNegativeTestData[index], options, false, 0);
+ }
+
+ const struct {
+ const char* string;
+ size_t length;
+ } kNULTestData[] = {
+ NUL_TEST_DATA("\0EXCEPTION_STATE_IDENTITY"),
+ NUL_TEST_DATA("EXCEPTION_\0STATE_IDENTITY"),
+ NUL_TEST_DATA("EXCEPTION_STATE\0_IDENTITY"),
+ NUL_TEST_DATA("EXCEPTION_STATE_IDENTITY\0"),
+ NUL_TEST_DATA("\0STATE_IDENTITY"),
+ NUL_TEST_DATA("STATE\0_IDENTITY"),
+ NUL_TEST_DATA("STATE_IDENTITY\0"),
+ NUL_TEST_DATA("\0003"),
+ NUL_TEST_DATA("3\0"),
+ NUL_TEST_DATA("0x8000000\0001"),
+ NUL_TEST_DATA("EXCEPTION_STATE_IDENTITY\0|MACH_EXCEPTION_CODES"),
+ NUL_TEST_DATA("EXCEPTION_STATE_IDENTITY|\0MACH_EXCEPTION_CODES"),
+ NUL_TEST_DATA("STATE_IDENTITY\0|MACH"),
+ NUL_TEST_DATA("STATE_IDENTITY|\0MACH"),
+ };
+
+ for (size_t index = 0; index < arraysize(kNULTestData); ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %zu", index));
+ base::StringPiece string(kNULTestData[index].string,
+ kNULTestData[index].length);
+ TestStringToExceptionBehavior(string, options, false, 0);
+ }
+ }
+
+ const struct {
+ const char* string;
+ StringToSymbolicConstantOptions options;
+ exception_behavior_t behavior;
+ } kNonCanonicalTestData[] = {
+ {"MACH_EXCEPTION_CODES|EXCEPTION_STATE_IDENTITY",
+ kAllowFullName,
+ implicit_cast<exception_behavior_t>(EXCEPTION_STATE_IDENTITY |
+ MACH_EXCEPTION_CODES)},
+ {"MACH|STATE_IDENTITY",
+ kAllowShortName,
+ implicit_cast<exception_behavior_t>(EXCEPTION_STATE_IDENTITY |
+ MACH_EXCEPTION_CODES)},
+ {"MACH_EXCEPTION_CODES|STATE",
+ kAllowFullName | kAllowShortName,
+ implicit_cast<exception_behavior_t>(EXCEPTION_STATE |
+ MACH_EXCEPTION_CODES)},
+ {"MACH|EXCEPTION_STATE",
+ kAllowFullName | kAllowShortName,
+ implicit_cast<exception_behavior_t>(EXCEPTION_STATE |
+ MACH_EXCEPTION_CODES)},
+ {"3|MACH_EXCEPTION_CODES",
+ kAllowFullName | kAllowNumber,
+ implicit_cast<exception_behavior_t>(MACH_EXCEPTION_CODES | 3)},
+ {"MACH|0x2",
+ kAllowShortName | kAllowNumber,
+ implicit_cast<exception_behavior_t>(MACH_EXCEPTION_CODES | 0x2)},
+ };
+
+ for (size_t index = 0; index < arraysize(kNonCanonicalTestData); ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %zu", index));
+ TestStringToExceptionBehavior(kNonCanonicalTestData[index].string,
+ kNonCanonicalTestData[index].options,
+ true,
+ kNonCanonicalTestData[index].behavior);
+ }
+
+ // Ensure that a NUL is not required at the end of the string.
+ {
+ SCOPED_TRACE("trailing_NUL_full");
+ TestStringToExceptionBehavior(base::StringPiece("EXCEPTION_DEFAULTS", 17),
+ kAllowFullName,
+ true,
+ EXCEPTION_DEFAULT);
+ }
+ {
+ SCOPED_TRACE("trailing_NUL_short");
+ TestStringToExceptionBehavior(base::StringPiece("DEFAULTS", 7),
+ kAllowShortName,
+ true,
+ EXCEPTION_DEFAULT);
+ }
+ {
+ SCOPED_TRACE("trailing_NUL_full_mach");
+ base::StringPiece string("EXCEPTION_DEFAULT|MACH_EXCEPTION_CODESS", 38);
+ TestStringToExceptionBehavior(string,
+ kAllowFullName | kAllowOr,
+ true,
+ EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES);
+ }
+ {
+ SCOPED_TRACE("trailing_NUL_short_mach");
+ TestStringToExceptionBehavior(base::StringPiece("DEFAULT|MACH_", 12),
+ kAllowShortName | kAllowOr,
+ true,
+ EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES);
+ }
+}
+
+const struct {
+ thread_state_flavor_t flavor;
+ const char* full_name;
+ const char* short_name;
+} kThreadStateFlavorTestData[] = {
+ {THREAD_STATE_NONE, "THREAD_STATE_NONE", "NONE"},
+ {THREAD_STATE_FLAVOR_LIST, "THREAD_STATE_FLAVOR_LIST", "FLAVOR_LIST"},
+ {THREAD_STATE_FLAVOR_LIST_NEW,
+ "THREAD_STATE_FLAVOR_LIST_NEW",
+ "FLAVOR_LIST_NEW"},
+ {THREAD_STATE_FLAVOR_LIST_10_9,
+ "THREAD_STATE_FLAVOR_LIST_10_9",
+ "FLAVOR_LIST_10_9"},
+#if defined(__i386__) || defined(__x86_64__)
+ {x86_THREAD_STATE32, "x86_THREAD_STATE32", "THREAD32"},
+ {x86_FLOAT_STATE32, "x86_FLOAT_STATE32", "FLOAT32"},
+ {x86_EXCEPTION_STATE32, "x86_EXCEPTION_STATE32", "EXCEPTION32"},
+ {x86_THREAD_STATE64, "x86_THREAD_STATE64", "THREAD64"},
+ {x86_FLOAT_STATE64, "x86_FLOAT_STATE64", "FLOAT64"},
+ {x86_EXCEPTION_STATE64, "x86_EXCEPTION_STATE64", "EXCEPTION64"},
+ {x86_THREAD_STATE, "x86_THREAD_STATE", "THREAD"},
+ {x86_FLOAT_STATE, "x86_FLOAT_STATE", "FLOAT"},
+ {x86_EXCEPTION_STATE, "x86_EXCEPTION_STATE", "EXCEPTION"},
+ {x86_DEBUG_STATE32, "x86_DEBUG_STATE32", "DEBUG32"},
+ {x86_DEBUG_STATE64, "x86_DEBUG_STATE64", "DEBUG64"},
+ {x86_DEBUG_STATE, "x86_DEBUG_STATE", "DEBUG"},
+ {14, "x86_SAVED_STATE32", "SAVED32"},
+ {15, "x86_SAVED_STATE64", "SAVED64"},
+ {x86_AVX_STATE32, "x86_AVX_STATE32", "AVX32"},
+ {x86_AVX_STATE64, "x86_AVX_STATE64", "AVX64"},
+ {x86_AVX_STATE, "x86_AVX_STATE", "AVX"},
+#elif defined(__ppc__) || defined(__ppc64__)
+ {PPC_THREAD_STATE, "PPC_THREAD_STATE", "THREAD"},
+ {PPC_FLOAT_STATE, "PPC_FLOAT_STATE", "FLOAT"},
+ {PPC_EXCEPTION_STATE, "PPC_EXCEPTION_STATE", "EXCEPTION"},
+ {PPC_VECTOR_STATE, "PPC_VECTOR_STATE", "VECTOR"},
+ {PPC_THREAD_STATE64, "PPC_THREAD_STATE64", "THREAD64"},
+ {PPC_EXCEPTION_STATE64, "PPC_EXCEPTION_STATE64", "EXCEPTION64"},
+#elif defined(__arm__) || defined(__arm64__)
+ {ARM_THREAD_STATE, "ARM_THREAD_STATE", "THREAD"},
+ {ARM_VFP_STATE, "ARM_VFP_STATE", "VFP"},
+ {ARM_EXCEPTION_STATE, "ARM_EXCEPTION_STATE", "EXCEPTION"},
+ {ARM_DEBUG_STATE, "ARM_DEBUG_STATE", "DEBUG"},
+ {ARM_THREAD_STATE64, "ARM_THREAD_STATE64", "THREAD64"},
+ {ARM_EXCEPTION_STATE64, "ARM_EXCEPTION_STATE64", "EXCEPTION64"},
+ {ARM_THREAD_STATE32, "ARM_THREAD_STATE32", "THREAD32"},
+ {ARM_DEBUG_STATE32, "ARM_DEBUG_STATE32", "DEBUG32"},
+ {ARM_DEBUG_STATE64, "ARM_DEBUG_STATE64", "DEBUG64"},
+ {ARM_NEON_STATE, "ARM_NEON_STATE", "NEON"},
+ {ARM_NEON_STATE64, "ARM_NEON_STATE64", "NEON64"},
+#endif
+};
+
+struct ConvertThreadStateFlavorTraits {
+ using ValueType = thread_state_flavor_t;
+ static std::string SomethingToString(
+ ValueType value,
+ SymbolicConstantToStringOptions options) {
+ return ThreadStateFlavorToString(value, options);
+ }
+ static bool StringToSomething(const base::StringPiece& string,
+ StringToSymbolicConstantOptions options,
+ ValueType* value) {
+ return StringToThreadStateFlavor(string, options, value);
+ }
+ static const char kValueName[];
+};
+const char ConvertThreadStateFlavorTraits::kValueName[] = "flavor";
+
+void TestThreadStateFlavorToString(exception_type_t value,
+ const char* expect_full,
+ const char* expect_short) {
+ return TestSomethingToString<ConvertThreadStateFlavorTraits>(
+ value, expect_full, expect_short);
+}
+
+TEST(SymbolicConstantsMach, ThreadStateFlavorToString) {
+ for (size_t index = 0;
+ index < arraysize(kThreadStateFlavorTestData);
+ ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %zu", index));
+ TestThreadStateFlavorToString(kThreadStateFlavorTestData[index].flavor,
+ kThreadStateFlavorTestData[index].full_name,
+ kThreadStateFlavorTestData[index].short_name);
+ }
+
+ for (thread_state_flavor_t flavor = 0; flavor < 136; ++flavor) {
+ SCOPED_TRACE(base::StringPrintf("flavor %d", flavor));
+
+ // Flavor numbers appear to be assigned somewhat haphazardly, especially on
+ // certain architectures. The conditional should match flavors that
+ // ThreadStateFlavorToString() knows how to convert.
+ if (
+#if defined(__i386__) || defined(__x86_64__)
+ flavor <= x86_AVX_STATE
+#elif defined(__ppc__) || defined(__ppc64__)
+ flavor <= THREAD_STATE_NONE
+#elif defined(__arm__) || defined(__arm64__)
+ (flavor <= ARM_EXCEPTION_STATE64 || flavor == ARM_THREAD_STATE32 ||
+ (flavor >= ARM_DEBUG_STATE32 && flavor <= ARM_NEON_STATE64))
+#endif
+ ||
+ flavor == THREAD_STATE_FLAVOR_LIST_NEW ||
+ flavor == THREAD_STATE_FLAVOR_LIST_10_9) {
+ TestThreadStateFlavorToString(flavor, "", "");
+ } else {
+ TestThreadStateFlavorToString(flavor, nullptr, nullptr);
+ }
+ }
+}
+
+void TestStringToThreadStateFlavor(const base::StringPiece& string,
+ StringToSymbolicConstantOptions options,
+ bool expect_result,
+ thread_state_flavor_t expect_value) {
+ return TestStringToSomething<ConvertThreadStateFlavorTraits>(
+ string, options, expect_result, expect_value);
+}
+
+TEST(SymbolicConstantsMach, StringToThreadStateFlavor) {
+ for (size_t option_index = 0;
+ option_index < arraysize(kNormalOptions);
+ ++option_index) {
+ SCOPED_TRACE(base::StringPrintf("option_index %zu", option_index));
+ StringToSymbolicConstantOptions options = kNormalOptions[option_index];
+ for (size_t index = 0;
+ index < arraysize(kThreadStateFlavorTestData);
+ ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %zu", index));
+ thread_state_flavor_t flavor = kThreadStateFlavorTestData[index].flavor;
+ {
+ SCOPED_TRACE("full_name");
+ TestStringToThreadStateFlavor(
+ kThreadStateFlavorTestData[index].full_name,
+ options,
+ options & kAllowFullName,
+ flavor);
+ }
+ {
+ SCOPED_TRACE("short_name");
+ TestStringToThreadStateFlavor(
+ kThreadStateFlavorTestData[index].short_name,
+ options,
+ options & kAllowShortName,
+ flavor);
+ }
+ {
+ SCOPED_TRACE("number");
+ std::string number_string = base::StringPrintf("%d", flavor);
+ TestStringToThreadStateFlavor(
+ number_string, options, options & kAllowNumber, flavor);
+ }
+ }
+
+ const char* const kNegativeTestData[] = {
+ "THREAD_STATE_NONE ",
+ " THREAD_STATE_NONE",
+ "NONE ",
+ " NONE",
+ "THREAD_STATE_THREAD_STATE_NONE",
+ "THREAD_STATE_NONE_AT_ALL",
+ "NONE_AT_ALL",
+ "THREAD_STATE_JUNK",
+ "JUNK",
+ "random",
+ " THREAD64",
+ "THREAD64 ",
+ "THREAD642",
+ "",
+#if defined(__i386__) || defined(__x86_64__)
+ " x86_THREAD_STATE64",
+ "x86_THREAD_STATE64 ",
+ "x86_THREAD_STATE642",
+ "x86_JUNK",
+ "x86_JUNK_STATE32",
+ "PPC_THREAD_STATE",
+ "ARM_THREAD_STATE",
+#elif defined(__ppc__) || defined(__ppc64__)
+ " PPC_THREAD_STATE64",
+ "PPC_THREAD_STATE64 ",
+ "PPC_THREAD_STATE642",
+ "PPC_JUNK",
+ "PPC_JUNK_STATE32",
+ "x86_THREAD_STATE",
+ "ARM_THREAD_STATE",
+#elif defined(__arm__) || defined(__arm64__)
+ " ARM_THREAD_STATE64",
+ "ARM_THREAD_STATE64 ",
+ "ARM_THREAD_STATE642",
+ "ARM_JUNK",
+ "ARM_JUNK_STATE32",
+ "x86_THREAD_STATE",
+ "PPC_THREAD_STATE",
+#endif
+ };
+
+ for (size_t index = 0; index < arraysize(kNegativeTestData); ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %zu", index));
+ TestStringToThreadStateFlavor(
+ kNegativeTestData[index], options, false, 0);
+ }
+
+ const struct {
+ const char* string;
+ size_t length;
+ } kNULTestData[] = {
+ NUL_TEST_DATA("\0THREAD_STATE_NONE"),
+ NUL_TEST_DATA("THREAD_\0STATE_NONE"),
+ NUL_TEST_DATA("THREAD_STATE_\0NONE"),
+ NUL_TEST_DATA("THREAD_STATE_NO\0NE"),
+ NUL_TEST_DATA("THREAD_STATE_NONE\0"),
+ NUL_TEST_DATA("\0NONE"),
+ NUL_TEST_DATA("NO\0NE"),
+ NUL_TEST_DATA("NONE\0"),
+ NUL_TEST_DATA("\0THREAD_STATE_FLAVOR_LIST_NEW"),
+ NUL_TEST_DATA("THREAD_STATE_\0FLAVOR_LIST_NEW"),
+ NUL_TEST_DATA("THREAD_STATE_FLAVOR_LIST\0_NEW"),
+ NUL_TEST_DATA("THREAD_STATE_FLAVOR_LIST_NEW\0"),
+ NUL_TEST_DATA("\0FLAVOR_LIST_NEW"),
+ NUL_TEST_DATA("FLAVOR_LIST\0_NEW"),
+ NUL_TEST_DATA("FLAVOR_LIST_NEW\0"),
+ NUL_TEST_DATA("\0THREAD"),
+ NUL_TEST_DATA("THR\0EAD"),
+ NUL_TEST_DATA("THREAD\0"),
+ NUL_TEST_DATA("\0THREAD64"),
+ NUL_TEST_DATA("THR\0EAD64"),
+ NUL_TEST_DATA("THREAD\064"),
+ NUL_TEST_DATA("THREAD64\0"),
+ NUL_TEST_DATA("\0002"),
+ NUL_TEST_DATA("2\0"),
+ NUL_TEST_DATA("1\0002"),
+#if defined(__i386__) || defined(__x86_64__)
+ NUL_TEST_DATA("\0x86_THREAD_STATE64"),
+ NUL_TEST_DATA("x86\0_THREAD_STATE64"),
+ NUL_TEST_DATA("x86_\0THREAD_STATE64"),
+ NUL_TEST_DATA("x86_THR\0EAD_STATE64"),
+ NUL_TEST_DATA("x86_THREAD\0_STATE64"),
+ NUL_TEST_DATA("x86_THREAD_\0STATE64"),
+ NUL_TEST_DATA("x86_THREAD_STA\0TE64"),
+ NUL_TEST_DATA("x86_THREAD_STATE\00064"),
+ NUL_TEST_DATA("x86_THREAD_STATE64\0"),
+#elif defined(__ppc__) || defined(__ppc64__)
+ NUL_TEST_DATA("\0PPC_THREAD_STATE64"),
+ NUL_TEST_DATA("PPC\0_THREAD_STATE64"),
+ NUL_TEST_DATA("PPC_\0THREAD_STATE64"),
+ NUL_TEST_DATA("PPC_THR\0EAD_STATE64"),
+ NUL_TEST_DATA("PPC_THREAD\0_STATE64"),
+ NUL_TEST_DATA("PPC_THREAD_\0STATE64"),
+ NUL_TEST_DATA("PPC_THREAD_STA\0TE64"),
+ NUL_TEST_DATA("PPC_THREAD_STATE\00064"),
+#elif defined(__arm__) || defined(__arm64__)
+ NUL_TEST_DATA("\0ARM_THREAD_STATE64"),
+ NUL_TEST_DATA("ARM\0_THREAD_STATE64"),
+ NUL_TEST_DATA("ARM_\0THREAD_STATE64"),
+ NUL_TEST_DATA("ARM_THR\0EAD_STATE64"),
+ NUL_TEST_DATA("ARM_THREAD\0_STATE64"),
+ NUL_TEST_DATA("ARM_THREAD_\0STATE64"),
+ NUL_TEST_DATA("ARM_THREAD_STA\0TE64"),
+ NUL_TEST_DATA("ARM_THREAD_STATE\00064"),
+#endif
+ };
+
+ for (size_t index = 0; index < arraysize(kNULTestData); ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %zu", index));
+ base::StringPiece string(kNULTestData[index].string,
+ kNULTestData[index].length);
+ TestStringToThreadStateFlavor(string, options, false, 0);
+ }
+ }
+
+ // Ensure that a NUL is not required at the end of the string.
+ {
+ SCOPED_TRACE("trailing_NUL_full");
+ TestStringToThreadStateFlavor(base::StringPiece("THREAD_STATE_NONER", 17),
+ kAllowFullName,
+ true,
+ THREAD_STATE_NONE);
+ }
+ {
+ SCOPED_TRACE("trailing_NUL_short");
+ TestStringToThreadStateFlavor(base::StringPiece("NONER", 4),
+ kAllowShortName,
+ true,
+ THREAD_STATE_NONE);
+ }
+ {
+ SCOPED_TRACE("trailing_NUL_full_new");
+ base::StringPiece string("THREAD_STATE_FLAVOR_LIST_NEWS", 28);
+ TestStringToThreadStateFlavor(
+ string, kAllowFullName, true, THREAD_STATE_FLAVOR_LIST_NEW);
+ }
+ {
+ SCOPED_TRACE("trailing_NUL_short_new");
+ TestStringToThreadStateFlavor(base::StringPiece("FLAVOR_LIST_NEWS", 15),
+ kAllowShortName,
+ true,
+ THREAD_STATE_FLAVOR_LIST_NEW);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/task_for_pid.cc b/chromium/third_party/crashpad/crashpad/util/mach/task_for_pid.cc
new file mode 100644
index 00000000000..1f238b19581
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/task_for_pid.cc
@@ -0,0 +1,167 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/task_for_pid.h"
+
+#include <sys/sysctl.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <iterator>
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/mac/mach_logging.h"
+#include "base/mac/scoped_mach_port.h"
+#include "util/posix/process_info.h"
+
+namespace crashpad {
+
+namespace {
+
+//! \brief Determines whether the groups that \a process_reader belongs to are
+//! a subset of the groups that the current process belongs to.
+//!
+//! This function is similar to 10.9.5
+//! `xnu-2422.115.4/bsd/kern/kern_credential.c` `kauth_cred_gid_subset()`.
+bool TaskForPIDGroupCheck(const ProcessInfo& process_info) {
+ std::set<gid_t> groups = process_info.AllGroups();
+
+ ProcessInfo process_info_self;
+ if (!process_info_self.Initialize(getpid())) {
+ return false;
+ }
+
+ std::set<gid_t> groups_self = process_info_self.AllGroups();
+
+ // difference will only contain elements of groups not present in groups_self.
+ // It will not contain elements of groups_self not present in groups. (That
+ // would be std::set_symmetric_difference.)
+ std::set<gid_t> difference;
+ std::set_difference(groups.begin(),
+ groups.end(),
+ groups_self.begin(),
+ groups_self.end(),
+ std::inserter(difference, difference.begin()));
+ if (!difference.empty()) {
+ LOG(ERROR) << "permission denied (gid)";
+ return false;
+ }
+
+ return true;
+}
+
+//! \brief Determines whether the current process should have permission to
+//! access the specified task port.
+//!
+//! This function is similar to 10.9.5
+//! `xnu-2422.115.4/bsd/vm/vm_unix.c` `task_for_pid_posix_check()`.
+//!
+//! This function accepts a `task_t` argument instead of a `pid_t` argument,
+//! implying that the task send right must be retrieved before it can be
+//! checked. This is done because a `pid_t` argument may refer to a different
+//! task in between the time that access is checked and its corresponding
+//! `task_t` is obtained by `task_for_pid()`. When `task_for_pid()` is called
+//! first, any operations requiring the process ID will call `pid_for_task()`
+//! and be guaranteed to use the process ID corresponding to the correct task,
+//! or to fail if that task is no longer running. If the task dies and the PID
+//! is recycled, it is still possible to look up the wrong PID, but falsely
+//! granting task access based on the new process’ characteristics is harmless
+//! because the task will be a dead name at that point.
+bool TaskForPIDCheck(task_t task) {
+ // If the effective user ID is not 0, then this code is not running as root at
+ // all, and the kernel’s own checks are sufficient to determine access. The
+ // point of this function is to simulate the kernel’s own checks when the
+ // effective user ID is 0 but the real user ID is anything else.
+ if (geteuid() != 0) {
+ return true;
+ }
+
+ // If the real user ID is 0, then this code is not running setuid root, it’s
+ // genuinely running as root, and it should be allowed maximum access.
+ uid_t uid = getuid();
+ if (uid == 0) {
+ return true;
+ }
+
+ // task_for_pid_posix_check() would permit access to the running process’ own
+ // task here, and would then check the kern.tfp.policy sysctl. If set to
+ // KERN_TFP_POLICY_DENY, it would deny access.
+ //
+ // This behavior is not duplicated here because the point of this function is
+ // to permit task_for_pid() access for setuid root programs. It is assumed
+ // that a setuid root program ought to be able to overcome any policy set in
+ // kern.tfp.policy.
+ //
+ // Access to the running process’ own task is not granted outright and is
+ // instead subjected to the same user/group ID checks as any other process.
+ // This has the effect of denying access to the running process’ own task when
+ // it is setuid root. This is intentional, because it prevents the same sort
+ // of cross-privilege disclosure discussed below at the DidChangePriveleges()
+ // check. The running process can still access its own task port via
+ // mach_task_self(), but a non-root user cannot coerce a setuid root tool to
+ // operate on itself by specifying its own process ID to this TaskForPID()
+ // interface.
+
+ ProcessInfo process_info;
+ if (!process_info.InitializeFromTask(task)) {
+ return false;
+ }
+
+ // The target process’ real user ID, effective user ID, and saved set-user ID
+ // must match this process’ own real user ID. task_for_pid_posix_check()
+ // checks against the current process’ effective user ID, but for the purposes
+ // of this function, when running setuid root, the real user ID is the correct
+ // choice.
+ if (process_info.RealUserID() != uid ||
+ process_info.EffectiveUserID() != uid ||
+ process_info.SavedUserID() != uid) {
+ LOG(ERROR) << "permission denied (uid)";
+ return false;
+ }
+
+ // The target process must not have changed privileges. The rationale for this
+ // check is explained in 10.9.5 xnu-2422.115.4/bsd/kern/kern_prot.c
+ // issetugid(): processes that have changed privileges may have loaded data
+ // using different credentials than they are currently operating with, and
+ // allowing other processes access to this data based solely on a check of the
+ // current credentials could violate confidentiality.
+ if (process_info.DidChangePrivileges()) {
+ LOG(ERROR) << "permission denied (P_SUGID)";
+ return false;
+ }
+
+ return TaskForPIDGroupCheck(process_info);
+}
+
+} // namespace
+
+task_t TaskForPID(pid_t pid) {
+ task_t task;
+ kern_return_t kr = task_for_pid(mach_task_self(), pid, &task);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "task_for_pid";
+ return TASK_NULL;
+ }
+
+ base::mac::ScopedMachSendRight task_owner(task);
+
+ if (!TaskForPIDCheck(task)) {
+ return TASK_NULL;
+ }
+
+ return task_owner.release();
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/task_for_pid.h b/chromium/third_party/crashpad/crashpad/util/mach/task_for_pid.h
new file mode 100644
index 00000000000..2b81fbebddd
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/task_for_pid.h
@@ -0,0 +1,59 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MACH_TASK_FOR_PID_H_
+#define CRASHPAD_UTIL_MACH_TASK_FOR_PID_H_
+
+#include <mach/mach.h>
+#include <sys/types.h>
+
+namespace crashpad {
+
+//! \brief Wraps `task_for_pid()`.
+//!
+//! This function exists to support `task_for_pid()` access checks in a setuid
+//! environment. Normally, `task_for_pid()` can only return an arbitrary task’s
+//! port when running as root or when taskgated(8) approves. When not running as
+//! root, a series of access checks are perfomed to ensure that the running
+//! process has permission to obtain the other process’ task port.
+//!
+//! It is possible to make an executable setuid root to give it broader
+//! `task_for_pid()` access by bypassing taskgated(8) checks, but this also has
+//! the effect of bypassing the access checks, allowing any process’ task port
+//! to be obtained. In most situations, these access checks are desirable to
+//! prevent security and privacy breaches.
+//!
+//! When running as setuid root, this function wraps `task_for_pid()`,
+//! reimplementing those access checks. A process whose effective user ID is 0
+//! and whose real user ID is nonzero is understood to be running setuid root.
+//! In this case, the requested task’s real, effective, and saved set-user IDs
+//! must all equal the running process’ real user ID, the requested task must
+//! not have changed privileges, and the requested task’s set of all group IDs
+//! (including its real, effective, and saved set-group IDs and supplementary
+//! group list) must be a subset of the running process’ set of all group IDs.
+//! These access checks mimic those that the kernel performs.
+//!
+//! When not running as setuid root, `task_for_pid()` is called directly,
+//! without imposing any additional checks beyond what the kernel does.
+//!
+//! \param[in] pid The process ID of the task whose task port is desired.
+//!
+//! \return A send right to the task port if it could be obtained, or
+//! `TASK_NULL` otherwise, with an error message logged. If a send right is
+//! returned, the caller takes ownership of it.
+task_t TaskForPID(pid_t pid);
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MACH_TASK_FOR_PID_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/task_memory.cc b/chromium/third_party/crashpad/crashpad/util/mach/task_memory.cc
new file mode 100644
index 00000000000..b87d02ea8f5
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/task_memory.cc
@@ -0,0 +1,158 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/task_memory.h"
+
+#include <mach/mach_vm.h>
+#include <string.h>
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "base/strings/stringprintf.h"
+#include "util/stdlib/strnlen.h"
+
+namespace crashpad {
+
+TaskMemory::MappedMemory::~MappedMemory() {
+}
+
+bool TaskMemory::MappedMemory::ReadCString(
+ size_t offset, std::string* string) const {
+ if (offset >= user_size_) {
+ LOG(WARNING) << "offset out of range";
+ return false;
+ }
+
+ const char* string_base = reinterpret_cast<const char*>(data_) + offset;
+ size_t max_length = user_size_ - offset;
+ size_t string_length = strnlen(string_base, max_length);
+ if (string_length == max_length) {
+ LOG(WARNING) << "unterminated string";
+ return false;
+ }
+
+ string->assign(string_base, string_length);
+ return true;
+}
+
+TaskMemory::MappedMemory::MappedMemory(vm_address_t vm_address,
+ size_t vm_size,
+ size_t user_offset,
+ size_t user_size)
+ : vm_(vm_address, vm_size),
+ data_(reinterpret_cast<const void*>(vm_address + user_offset)),
+ user_size_(user_size) {
+ vm_address_t vm_end = vm_address + vm_size;
+ vm_address_t user_address = reinterpret_cast<vm_address_t>(data_);
+ vm_address_t user_end = user_address + user_size;
+ DCHECK_GE(user_address, vm_address);
+ DCHECK_LE(user_address, vm_end);
+ DCHECK_GE(user_end, vm_address);
+ DCHECK_LE(user_end, vm_end);
+}
+
+TaskMemory::TaskMemory(task_t task) : task_(task) {
+}
+
+bool TaskMemory::Read(mach_vm_address_t address, size_t size, void* buffer) {
+ scoped_ptr<MappedMemory> memory = ReadMapped(address, size);
+ if (!memory) {
+ return false;
+ }
+
+ memcpy(buffer, memory->data(), size);
+ return true;
+}
+
+scoped_ptr<TaskMemory::MappedMemory> TaskMemory::ReadMapped(
+ mach_vm_address_t address, size_t size) {
+ if (size == 0) {
+ return scoped_ptr<MappedMemory>(new MappedMemory(0, 0, 0, 0));
+ }
+
+ mach_vm_address_t region_address = mach_vm_trunc_page(address);
+ mach_vm_size_t region_size =
+ mach_vm_round_page(address - region_address + size);
+
+ vm_offset_t region;
+ mach_msg_type_number_t region_count;
+ kern_return_t kr =
+ mach_vm_read(task_, region_address, region_size, &region, &region_count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(WARNING, kr) << base::StringPrintf(
+ "mach_vm_read(0x%llx, 0x%llx)", region_address, region_size);
+ return scoped_ptr<MappedMemory>();
+ }
+
+ DCHECK_EQ(region_count, region_size);
+ return scoped_ptr<MappedMemory>(
+ new MappedMemory(region, region_size, address - region_address, size));
+}
+
+bool TaskMemory::ReadCString(mach_vm_address_t address, std::string* string) {
+ return ReadCStringInternal(address, false, 0, string);
+}
+
+bool TaskMemory::ReadCStringSizeLimited(mach_vm_address_t address,
+ mach_vm_size_t size,
+ std::string* string) {
+ return ReadCStringInternal(address, true, size, string);
+}
+
+bool TaskMemory::ReadCStringInternal(mach_vm_address_t address,
+ bool has_size,
+ mach_vm_size_t size,
+ std::string* string) {
+ if (has_size) {
+ if (size == 0) {
+ string->clear();
+ return true;
+ }
+ } else {
+ size = PAGE_SIZE;
+ }
+
+ std::string local_string;
+ mach_vm_address_t read_address = address;
+ do {
+ mach_vm_size_t read_length =
+ std::min(size, PAGE_SIZE - (read_address % PAGE_SIZE));
+ scoped_ptr<MappedMemory> read_region =
+ ReadMapped(read_address, read_length);
+ if (!read_region) {
+ return false;
+ }
+
+ const char* read_region_data =
+ reinterpret_cast<const char*>(read_region->data());
+ size_t read_region_data_length = strnlen(read_region_data, read_length);
+ local_string.append(read_region_data, read_region_data_length);
+ if (read_region_data_length < read_length) {
+ string->swap(local_string);
+ return true;
+ }
+
+ if (has_size) {
+ size -= read_length;
+ }
+ read_address = mach_vm_trunc_page(read_address + read_length);
+ } while ((!has_size || size > 0) && read_address > address);
+
+ LOG(WARNING) << base::StringPrintf("unterminated string at 0x%llx", address);
+ return false;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/task_memory.h b/chromium/third_party/crashpad/crashpad/util/mach/task_memory.h
new file mode 100644
index 00000000000..2077d5eadd2
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/task_memory.h
@@ -0,0 +1,177 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MACH_TASK_MEMORY_H_
+#define CRASHPAD_UTIL_MACH_TASK_MEMORY_H_
+
+#include <mach/mach.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/mac/scoped_mach_vm.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace crashpad {
+
+//! \brief Accesses the memory of another Mach task.
+class TaskMemory {
+ public:
+ //! \brief A memory region mapped from another Mach task.
+ //!
+ //! The mapping is maintained until this object is destroyed.
+ class MappedMemory {
+ public:
+ ~MappedMemory();
+
+ //! \brief Returns a pointer to the data requested by the user.
+ //!
+ //! This is the value of the \a vm_address + \a user_offset parameters
+ //! passed to the constructor, casted to `const void*`.
+ const void* data() const { return data_; }
+
+ //! \brief Reads a `NUL`-terminated C string from the mapped region.
+ //!
+ //! This method will read contiguous memory until a `NUL` terminator is
+ //! found.
+ //!
+ //! \param[in] offset The offset into data() of the string to be read.
+ //! \param[out] string The string, whose contents begin at data() and
+ //! continue up to a `NUL` terminator.
+ //!
+ //! \return `true` on success, with \a string set appropriately. If \a
+ //! offset is greater than or equal to the \a user_size constructor
+ //! parameter, or if no `NUL` terminator was found in data() after \a
+ //! offset, returns `false` with an appropriate warning logged.
+ bool ReadCString(size_t offset, std::string* string) const;
+
+ private:
+ //! \brief Creates an object that owns a memory region mapped from another
+ //! Mach task.
+ //!
+ //! \param[in] vm_address The address in this process’ address space where
+ //! the mapping begins. This must be page-aligned.
+ //! \param[in] vm_size The total size of the mapping that begins at \a
+ //! vm_address. This must be page-aligned.
+ //! \param[in] user_offset The offset into the mapped region where the data
+ //! requested by the user begins. This accounts for the fact that a
+ //! mapping must be page-aligned but the user data may not be. This
+ //! parameter must be equal to or less than \a vm_size.
+ //! \param[in] user_size The size of the data requested by the user. This
+ //! parameter can be used to compute the end address of user data, which
+ //! must be within the mapped region.
+ MappedMemory(vm_address_t vm_address,
+ size_t vm_size,
+ size_t user_offset,
+ size_t user_size);
+
+ base::mac::ScopedMachVM vm_;
+ const void* data_;
+ size_t user_size_;
+
+ // The outer class needs to be able to call this class’ private constructor.
+ friend class TaskMemory;
+
+ DISALLOW_COPY_AND_ASSIGN(MappedMemory);
+ };
+
+ //! \param[in] task A send right to the target task’s task port. This object
+ //! does not take ownership of the send right.
+ explicit TaskMemory(task_t task);
+
+ ~TaskMemory() {}
+
+ //! \brief Copies memory from the target task into a caller-provided buffer in
+ //! the current task.
+ //!
+ //! \param[in] address The address, in the target task’s address space, of the
+ //! memory region to copy.
+ //! \param[in] size The size, in bytes, of the memory region to copy. \a
+ //! buffer must be at least this size.
+ //! \param[out] buffer The buffer into which the contents of the other task’s
+ //! memory will be copied.
+ //!
+ //! \return `true` on success, with \a buffer filled appropriately. `false` on
+ //! failure, with a warning logged. Failures can occur, for example, when
+ //! encountering unmapped or unreadable pages.
+ //!
+ //! \sa ReadMapped()
+ bool Read(mach_vm_address_t address, size_t size, void* buffer);
+
+ //! \brief Maps memory from the target task into the current task.
+ //!
+ //! This interface is an alternative to Read() that does not require the
+ //! caller to provide a buffer to fill. This avoids copying memory, which can
+ //! offer a performance improvement.
+ //!
+ //! \param[in] address The address, in the target task’s address space, of the
+ //! memory region to map.
+ //! \param[in] size The size, in bytes, of the memory region to map.
+ //!
+ //! \return On success, a MappedMemory object that provides access to the data
+ //! requested. On faliure, `nullptr`, with a warning logged. Failures can
+ //! occur, for example, when encountering unmapped or unreadable pages.
+ scoped_ptr<MappedMemory> ReadMapped(mach_vm_address_t address, size_t size);
+
+ //! \brief Reads a `NUL`-terminated C string from the target task into a
+ //! string in the current task.
+ //!
+ //! The length of the string need not be known ahead of time. This method will
+ //! read contiguous memory until a `NUL` terminator is found.
+ //!
+ //! \param[in] address The address, in the target task’s address space, of the
+ //! string to copy.
+ //! \param[out] string The string read from the other task.
+ //!
+ //! \return `true` on success, with \a string set appropriately. `false` on
+ //! failure, with a warning logged. Failures can occur, for example, when
+ //! encountering unmapped or unreadable pages.
+ //!
+ //! \sa MappedMemory::ReadCString()
+ bool ReadCString(mach_vm_address_t address, std::string* string);
+
+ //! \brief Reads a `NUL`-terminated C string from the target task into a
+ //! string in the current task.
+ //!
+ //! \param[in] address The address, in the target task’s address space, of the
+ //! string to copy.
+ //! \param[in] size The maximum number of bytes to read. The string is
+ //! required to be `NUL`-terminated within this many bytes.
+ //! \param[out] string The string read from the other task.
+ //!
+ //! \return `true` on success, with \a string set appropriately. `false` on
+ //! failure, with a warning logged. Failures can occur, for example, when
+ //! a `NUL` terminator is not found within \a size bytes, or when
+ //! encountering unmapped or unreadable pages.
+ //!
+ //! \sa MappedMemory::ReadCString()
+ bool ReadCStringSizeLimited(mach_vm_address_t address,
+ mach_vm_size_t size,
+ std::string* string);
+
+ private:
+ // The common internal implementation shared by the ReadCString*() methods.
+ bool ReadCStringInternal(mach_vm_address_t address,
+ bool has_size,
+ mach_vm_size_t size,
+ std::string* string);
+
+ task_t task_; // weak
+
+ DISALLOW_COPY_AND_ASSIGN(TaskMemory);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MACH_TASK_MEMORY_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/mach/task_memory_test.cc b/chromium/third_party/crashpad/crashpad/util/mach/task_memory_test.cc
new file mode 100644
index 00000000000..2247ed2fab7
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/mach/task_memory_test.cc
@@ -0,0 +1,555 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/task_memory.h"
+
+#include <mach/mach.h>
+#include <string.h>
+
+#include <algorithm>
+#include <string>
+
+#include "base/mac/scoped_mach_port.h"
+#include "base/mac/scoped_mach_vm.h"
+#include "base/memory/scoped_ptr.h"
+#include "gtest/gtest.h"
+#include "test/mac/mach_errors.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(TaskMemory, ReadSelf) {
+ vm_address_t address = 0;
+ const vm_size_t kSize = 4 * PAGE_SIZE;
+ kern_return_t kr =
+ vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE);
+ ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_allocate");
+ base::mac::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize));
+
+ char* region = reinterpret_cast<char*>(address);
+ for (size_t index = 0; index < kSize; ++index) {
+ region[index] = (index % 256) ^ ((index >> 8) % 256);
+ }
+
+ TaskMemory memory(mach_task_self());
+
+ // This tests using both the Read() and ReadMapped() interfaces.
+ std::string result(kSize, '\0');
+ scoped_ptr<TaskMemory::MappedMemory> mapped;
+
+ // Ensure that the entire region can be read.
+ ASSERT_TRUE(memory.Read(address, kSize, &result[0]));
+ EXPECT_EQ(0, memcmp(region, &result[0], kSize));
+ ASSERT_TRUE((mapped = memory.ReadMapped(address, kSize)));
+ EXPECT_EQ(0, memcmp(region, mapped->data(), kSize));
+
+ // Ensure that a read of length 0 succeeds and doesn’t touch the result.
+ result.assign(kSize, '\0');
+ std::string zeroes = result;
+ ASSERT_TRUE(memory.Read(address, 0, &result[0]));
+ EXPECT_EQ(zeroes, result);
+ ASSERT_TRUE((mapped = memory.ReadMapped(address, 0)));
+
+ // Ensure that a read starting at an unaligned address works.
+ ASSERT_TRUE(memory.Read(address + 1, kSize - 1, &result[0]));
+ EXPECT_EQ(0, memcmp(region + 1, &result[0], kSize - 1));
+ ASSERT_TRUE((mapped = memory.ReadMapped(address + 1, kSize - 1)));
+ EXPECT_EQ(0, memcmp(region + 1, mapped->data(), kSize - 1));
+
+ // Ensure that a read ending at an unaligned address works.
+ ASSERT_TRUE(memory.Read(address, kSize - 1, &result[0]));
+ EXPECT_EQ(0, memcmp(region, &result[0], kSize - 1));
+ ASSERT_TRUE((mapped = memory.ReadMapped(address, kSize - 1)));
+ EXPECT_EQ(0, memcmp(region, mapped->data(), kSize - 1));
+
+ // Ensure that a read starting and ending at unaligned addresses works.
+ ASSERT_TRUE(memory.Read(address + 1, kSize - 2, &result[0]));
+ EXPECT_EQ(0, memcmp(region + 1, &result[0], kSize - 2));
+ ASSERT_TRUE((mapped = memory.ReadMapped(address + 1, kSize - 2)));
+ EXPECT_EQ(0, memcmp(region + 1, mapped->data(), kSize - 2));
+
+ // Ensure that a read of exactly one page works.
+ ASSERT_TRUE(memory.Read(address + PAGE_SIZE, PAGE_SIZE, &result[0]));
+ EXPECT_EQ(0, memcmp(region + PAGE_SIZE, &result[0], PAGE_SIZE));
+ ASSERT_TRUE((mapped = memory.ReadMapped(address + PAGE_SIZE, PAGE_SIZE)));
+ EXPECT_EQ(0, memcmp(region + PAGE_SIZE, mapped->data(), PAGE_SIZE));
+
+ // Ensure that a read of a single byte works.
+ ASSERT_TRUE(memory.Read(address + 2, 1, &result[0]));
+ EXPECT_EQ(region[2], result[0]);
+ ASSERT_TRUE((mapped = memory.ReadMapped(address + 2, 1)));
+ EXPECT_EQ(region[2], reinterpret_cast<const char*>(mapped->data())[0]);
+
+ // Ensure that a read of length zero works and doesn’t touch the data.
+ result[0] = 'M';
+ ASSERT_TRUE(memory.Read(address + 3, 0, &result[0]));
+ EXPECT_EQ('M', result[0]);
+ ASSERT_TRUE((mapped = memory.ReadMapped(address + 3, 0)));
+}
+
+TEST(TaskMemory, ReadSelfUnmapped) {
+ vm_address_t address = 0;
+ const vm_size_t kSize = 2 * PAGE_SIZE;
+ kern_return_t kr =
+ vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE);
+ ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_allocate");
+ base::mac::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize));
+
+ char* region = reinterpret_cast<char*>(address);
+ for (size_t index = 0; index < kSize; ++index) {
+ // Don’t include any NUL bytes, because ReadCString stops when it encounters
+ // a NUL.
+ region[index] = (index % 255) + 1;
+ }
+
+ kr = vm_protect(
+ mach_task_self(), address + PAGE_SIZE, PAGE_SIZE, FALSE, VM_PROT_NONE);
+ ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_protect");
+
+ TaskMemory memory(mach_task_self());
+ std::string result(kSize, '\0');
+
+ EXPECT_FALSE(memory.Read(address, kSize, &result[0]));
+ EXPECT_FALSE(memory.Read(address + 1, kSize - 1, &result[0]));
+ EXPECT_FALSE(memory.Read(address + PAGE_SIZE, 1, &result[0]));
+ EXPECT_FALSE(memory.Read(address + PAGE_SIZE - 1, 2, &result[0]));
+ EXPECT_TRUE(memory.Read(address, PAGE_SIZE, &result[0]));
+ EXPECT_TRUE(memory.Read(address + PAGE_SIZE - 1, 1, &result[0]));
+
+ // Do the same thing with the ReadMapped() interface.
+ scoped_ptr<TaskMemory::MappedMemory> mapped;
+ EXPECT_FALSE((mapped = memory.ReadMapped(address, kSize)));
+ EXPECT_FALSE((mapped = memory.ReadMapped(address + 1, kSize - 1)));
+ EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE, 1)));
+ EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 2)));
+ EXPECT_TRUE((mapped = memory.ReadMapped(address, PAGE_SIZE)));
+ EXPECT_TRUE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 1)));
+
+ // Repeat the test with an unmapped page instead of an unreadable one. This
+ // portion of the test may be flaky in the presence of other threads, if
+ // another thread maps something in the region that is deallocated here.
+ kr = vm_deallocate(mach_task_self(), address + PAGE_SIZE, PAGE_SIZE);
+ ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_deallocate");
+ vm_owner.reset(address, PAGE_SIZE);
+
+ EXPECT_FALSE(memory.Read(address, kSize, &result[0]));
+ EXPECT_FALSE(memory.Read(address + 1, kSize - 1, &result[0]));
+ EXPECT_FALSE(memory.Read(address + PAGE_SIZE, 1, &result[0]));
+ EXPECT_FALSE(memory.Read(address + PAGE_SIZE - 1, 2, &result[0]));
+ EXPECT_TRUE(memory.Read(address, PAGE_SIZE, &result[0]));
+ EXPECT_TRUE(memory.Read(address + PAGE_SIZE - 1, 1, &result[0]));
+
+ // Do the same thing with the ReadMapped() interface.
+ EXPECT_FALSE((mapped = memory.ReadMapped(address, kSize)));
+ EXPECT_FALSE((mapped = memory.ReadMapped(address + 1, kSize - 1)));
+ EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE, 1)));
+ EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 2)));
+ EXPECT_TRUE((mapped = memory.ReadMapped(address, PAGE_SIZE)));
+ EXPECT_TRUE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 1)));
+}
+
+// This function consolidates the cast from a char* to mach_vm_address_t in one
+// location when reading from the current task.
+bool ReadCStringSelf(TaskMemory* memory,
+ const char* pointer,
+ std::string* result) {
+ return memory->ReadCString(reinterpret_cast<mach_vm_address_t>(pointer),
+ result);
+}
+
+TEST(TaskMemory, ReadCStringSelf) {
+ TaskMemory memory(mach_task_self());
+ std::string result;
+
+ const char kConstCharEmpty[] = "";
+ ASSERT_TRUE(ReadCStringSelf(&memory, kConstCharEmpty, &result));
+ EXPECT_TRUE(result.empty());
+ EXPECT_EQ(kConstCharEmpty, result);
+
+ const char kConstCharShort[] = "A short const char[]";
+ ASSERT_TRUE(ReadCStringSelf(&memory, kConstCharShort, &result));
+ EXPECT_FALSE(result.empty());
+ EXPECT_EQ(kConstCharShort, result);
+
+ static const char kStaticConstCharEmpty[] = "";
+ ASSERT_TRUE(ReadCStringSelf(&memory, kStaticConstCharEmpty, &result));
+ EXPECT_TRUE(result.empty());
+ EXPECT_EQ(kStaticConstCharEmpty, result);
+
+ static const char kStaticConstCharShort[] = "A short static const char[]";
+ ASSERT_TRUE(ReadCStringSelf(&memory, kStaticConstCharShort, &result));
+ EXPECT_FALSE(result.empty());
+ EXPECT_EQ(kStaticConstCharShort, result);
+
+ std::string string_short("A short std::string in a function");
+ ASSERT_TRUE(ReadCStringSelf(&memory, &string_short[0], &result));
+ EXPECT_FALSE(result.empty());
+ EXPECT_EQ(string_short, result);
+
+ std::string string_long;
+ const size_t kStringLongSize = 4 * PAGE_SIZE;
+ for (size_t index = 0; index < kStringLongSize; ++index) {
+ // Don’t include any NUL bytes, because ReadCString stops when it encounters
+ // a NUL.
+ string_long.append(1, (index % 255) + 1);
+ }
+ ASSERT_EQ(kStringLongSize, string_long.size());
+ ASSERT_TRUE(ReadCStringSelf(&memory, &string_long[0], &result));
+ EXPECT_FALSE(result.empty());
+ EXPECT_EQ(kStringLongSize, result.size());
+ EXPECT_EQ(string_long, result);
+}
+
+TEST(TaskMemory, ReadCStringSelfUnmapped) {
+ vm_address_t address = 0;
+ const vm_size_t kSize = 2 * PAGE_SIZE;
+ kern_return_t kr =
+ vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE);
+ ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_allocate");
+ base::mac::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize));
+
+ char* region = reinterpret_cast<char*>(address);
+ for (size_t index = 0; index < kSize; ++index) {
+ // Don’t include any NUL bytes, because ReadCString stops when it encounters
+ // a NUL.
+ region[index] = (index % 255) + 1;
+ }
+
+ kr = vm_protect(
+ mach_task_self(), address + PAGE_SIZE, PAGE_SIZE, FALSE, VM_PROT_NONE);
+ ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_protect");
+
+ TaskMemory memory(mach_task_self());
+ std::string result;
+ EXPECT_FALSE(memory.ReadCString(address, &result));
+
+ // Make sure that if the string is NUL-terminated within the mapped memory
+ // region, it can be read properly.
+ char terminator_or_not = '\0';
+ std::swap(region[PAGE_SIZE - 1], terminator_or_not);
+ ASSERT_TRUE(memory.ReadCString(address, &result));
+ EXPECT_FALSE(result.empty());
+ EXPECT_EQ(PAGE_SIZE - 1u, result.size());
+ EXPECT_EQ(region, result);
+
+ // Repeat the test with an unmapped page instead of an unreadable one. This
+ // portion of the test may be flaky in the presence of other threads, if
+ // another thread maps something in the region that is deallocated here.
+ std::swap(region[PAGE_SIZE - 1], terminator_or_not);
+ kr = vm_deallocate(mach_task_self(), address + PAGE_SIZE, PAGE_SIZE);
+ ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_deallocate");
+ vm_owner.reset(address, PAGE_SIZE);
+
+ EXPECT_FALSE(memory.ReadCString(address, &result));
+
+ // Clear the result before testing that the string can be read. This makes
+ // sure that the result is actually filled in, because it already contains the
+ // expected value from the tests above.
+ result.clear();
+ std::swap(region[PAGE_SIZE - 1], terminator_or_not);
+ ASSERT_TRUE(memory.ReadCString(address, &result));
+ EXPECT_FALSE(result.empty());
+ EXPECT_EQ(PAGE_SIZE - 1u, result.size());
+ EXPECT_EQ(region, result);
+}
+
+// This function consolidates the cast from a char* to mach_vm_address_t in one
+// location when reading from the current task.
+bool ReadCStringSizeLimitedSelf(TaskMemory* memory,
+ const char* pointer,
+ size_t size,
+ std::string* result) {
+ return memory->ReadCStringSizeLimited(
+ reinterpret_cast<mach_vm_address_t>(pointer), size, result);
+}
+
+TEST(TaskMemory, ReadCStringSizeLimited_ConstCharEmpty) {
+ TaskMemory memory(mach_task_self());
+ std::string result;
+
+ const char kConstCharEmpty[] = "";
+ ASSERT_TRUE(ReadCStringSizeLimitedSelf(
+ &memory, kConstCharEmpty, arraysize(kConstCharEmpty), &result));
+ EXPECT_TRUE(result.empty());
+ EXPECT_EQ(kConstCharEmpty, result);
+
+ result.clear();
+ ASSERT_TRUE(ReadCStringSizeLimitedSelf(
+ &memory, kConstCharEmpty, arraysize(kConstCharEmpty) + 1, &result));
+ EXPECT_TRUE(result.empty());
+ EXPECT_EQ(kConstCharEmpty, result);
+
+ result.clear();
+ ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, kConstCharEmpty, 0, &result));
+ EXPECT_TRUE(result.empty());
+ EXPECT_EQ(kConstCharEmpty, result);
+}
+
+TEST(TaskMemory, ReadCStringSizeLimited_ConstCharShort) {
+ TaskMemory memory(mach_task_self());
+ std::string result;
+
+ const char kConstCharShort[] = "A short const char[]";
+ ASSERT_TRUE(ReadCStringSizeLimitedSelf(
+ &memory, kConstCharShort, arraysize(kConstCharShort), &result));
+ EXPECT_FALSE(result.empty());
+ EXPECT_EQ(kConstCharShort, result);
+
+ result.clear();
+ ASSERT_TRUE(ReadCStringSizeLimitedSelf(
+ &memory, kConstCharShort, arraysize(kConstCharShort) + 1, &result));
+ EXPECT_FALSE(result.empty());
+ EXPECT_EQ(kConstCharShort, result);
+
+ ASSERT_FALSE(ReadCStringSizeLimitedSelf(
+ &memory, kConstCharShort, arraysize(kConstCharShort) - 1, &result));
+}
+
+TEST(TaskMemory, ReadCStringSizeLimited_StaticConstCharEmpty) {
+ TaskMemory memory(mach_task_self());
+ std::string result;
+
+ static const char kStaticConstCharEmpty[] = "";
+ ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory,
+ kStaticConstCharEmpty,
+ arraysize(kStaticConstCharEmpty),
+ &result));
+ EXPECT_TRUE(result.empty());
+ EXPECT_EQ(kStaticConstCharEmpty, result);
+
+ result.clear();
+ ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory,
+ kStaticConstCharEmpty,
+ arraysize(kStaticConstCharEmpty) + 1,
+ &result));
+ EXPECT_TRUE(result.empty());
+ EXPECT_EQ(kStaticConstCharEmpty, result);
+
+ result.clear();
+ ASSERT_TRUE(
+ ReadCStringSizeLimitedSelf(&memory, kStaticConstCharEmpty, 0, &result));
+ EXPECT_TRUE(result.empty());
+ EXPECT_EQ(kStaticConstCharEmpty, result);
+}
+
+TEST(TaskMemory, ReadCStringSizeLimited_StaticConstCharShort) {
+ TaskMemory memory(mach_task_self());
+ std::string result;
+
+ static const char kStaticConstCharShort[] = "A short static const char[]";
+ ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory,
+ kStaticConstCharShort,
+ arraysize(kStaticConstCharShort),
+ &result));
+ EXPECT_FALSE(result.empty());
+ EXPECT_EQ(kStaticConstCharShort, result);
+
+ result.clear();
+ ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory,
+ kStaticConstCharShort,
+ arraysize(kStaticConstCharShort) + 1,
+ &result));
+ EXPECT_FALSE(result.empty());
+ EXPECT_EQ(kStaticConstCharShort, result);
+
+ ASSERT_FALSE(ReadCStringSizeLimitedSelf(&memory,
+ kStaticConstCharShort,
+ arraysize(kStaticConstCharShort) - 1,
+ &result));
+}
+
+TEST(TaskMemory, ReadCStringSizeLimited_StringShort) {
+ TaskMemory memory(mach_task_self());
+ std::string result;
+
+ std::string string_short("A short std::string in a function");
+ ASSERT_TRUE(ReadCStringSizeLimitedSelf(
+ &memory, &string_short[0], string_short.size() + 1, &result));
+ EXPECT_FALSE(result.empty());
+ EXPECT_EQ(string_short, result);
+
+ result.clear();
+ ASSERT_TRUE(ReadCStringSizeLimitedSelf(
+ &memory, &string_short[0], string_short.size() + 2, &result));
+ EXPECT_FALSE(result.empty());
+ EXPECT_EQ(string_short, result);
+
+ ASSERT_FALSE(ReadCStringSizeLimitedSelf(
+ &memory, &string_short[0], string_short.size(), &result));
+}
+
+TEST(TaskMemory, ReadCStringSizeLimited_StringLong) {
+ TaskMemory memory(mach_task_self());
+ std::string result;
+
+ std::string string_long;
+ const size_t kStringLongSize = 4 * PAGE_SIZE;
+ for (size_t index = 0; index < kStringLongSize; ++index) {
+ // Don’t include any NUL bytes, because ReadCString stops when it encounters
+ // a NUL.
+ string_long.append(1, (index % 255) + 1);
+ }
+ ASSERT_EQ(kStringLongSize, string_long.size());
+ ASSERT_TRUE(ReadCStringSizeLimitedSelf(
+ &memory, &string_long[0], string_long.size() + 1, &result));
+ EXPECT_FALSE(result.empty());
+ EXPECT_EQ(kStringLongSize, result.size());
+ EXPECT_EQ(string_long, result);
+
+ result.clear();
+ ASSERT_TRUE(ReadCStringSizeLimitedSelf(
+ &memory, &string_long[0], string_long.size() + 2, &result));
+ EXPECT_FALSE(result.empty());
+ EXPECT_EQ(kStringLongSize, result.size());
+ EXPECT_EQ(string_long, result);
+
+ ASSERT_FALSE(ReadCStringSizeLimitedSelf(
+ &memory, &string_long[0], string_long.size(), &result));
+}
+
+bool IsAddressMapped(vm_address_t address) {
+ vm_address_t region_address = address;
+ vm_size_t region_size;
+ mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64;
+ vm_region_basic_info_64 info;
+ mach_port_t object;
+ kern_return_t kr = vm_region_64(mach_task_self(),
+ &region_address,
+ &region_size,
+ VM_REGION_BASIC_INFO_64,
+ reinterpret_cast<vm_region_info_t>(&info),
+ &count,
+ &object);
+ if (kr == KERN_SUCCESS) {
+ // |object| will be MACH_PORT_NULL (10.9.4 xnu-2422.110.17/osfmk/vm/vm_map.c
+ // vm_map_region()), but the interface acts as if it might carry a send
+ // right, so treat it as documented.
+ base::mac::ScopedMachSendRight object_owner(object);
+
+ return address >= region_address && address <= region_address + region_size;
+ }
+
+ if (kr == KERN_INVALID_ADDRESS) {
+ return false;
+ }
+
+ ADD_FAILURE() << MachErrorMessage(kr, "vm_region_64");;
+ return false;
+}
+
+TEST(TaskMemory, MappedMemoryDeallocates) {
+ // This tests that once a TaskMemory::MappedMemory object is destroyed, it
+ // releases the mapped memory that it owned. Technically, this test is not
+ // valid because after the mapping is released, something else (on another
+ // thread) might wind up mapped in the same address. In the test environment,
+ // hopefully there are either no other threads or they’re all quiescent, so
+ // nothing else should wind up mapped in the address.
+
+ TaskMemory memory(mach_task_self());
+ scoped_ptr<TaskMemory::MappedMemory> mapped;
+
+ static const char kTestBuffer[] = "hello!";
+ mach_vm_address_t test_address =
+ reinterpret_cast<mach_vm_address_t>(&kTestBuffer);
+ ASSERT_TRUE((mapped = memory.ReadMapped(test_address, sizeof(kTestBuffer))));
+ EXPECT_EQ(0, memcmp(kTestBuffer, mapped->data(), sizeof(kTestBuffer)));
+
+ vm_address_t mapped_address = reinterpret_cast<vm_address_t>(mapped->data());
+ EXPECT_TRUE(IsAddressMapped(mapped_address));
+
+ mapped.reset();
+ EXPECT_FALSE(IsAddressMapped(mapped_address));
+
+ // This is the same but with a big buffer that’s definitely larger than a
+ // single page. This makes sure that the whole mapped region winds up being
+ // deallocated.
+ const size_t kBigSize = 4 * PAGE_SIZE;
+ scoped_ptr<char[]> big_buffer(new char[kBigSize]);
+ test_address = reinterpret_cast<mach_vm_address_t>(&big_buffer[0]);
+ ASSERT_TRUE((mapped = memory.ReadMapped(test_address, kBigSize)));
+
+ mapped_address = reinterpret_cast<vm_address_t>(mapped->data());
+ vm_address_t mapped_last_address = mapped_address + kBigSize - 1;
+ EXPECT_TRUE(IsAddressMapped(mapped_address));
+ EXPECT_TRUE(IsAddressMapped(mapped_address + PAGE_SIZE));
+ EXPECT_TRUE(IsAddressMapped(mapped_last_address));
+
+ mapped.reset();
+ EXPECT_FALSE(IsAddressMapped(mapped_address));
+ EXPECT_FALSE(IsAddressMapped(mapped_address + PAGE_SIZE));
+ EXPECT_FALSE(IsAddressMapped(mapped_last_address));
+}
+
+TEST(TaskMemory, MappedMemoryReadCString) {
+ // This tests the behavior of TaskMemory::MappedMemory::ReadCString().
+ TaskMemory memory(mach_task_self());
+ scoped_ptr<TaskMemory::MappedMemory> mapped;
+
+ static const char kTestBuffer[] = "0\0" "2\0" "45\0" "789";
+ const mach_vm_address_t kTestAddress =
+ reinterpret_cast<mach_vm_address_t>(&kTestBuffer);
+ ASSERT_TRUE((mapped = memory.ReadMapped(kTestAddress, 10)));
+
+ std::string string;
+ ASSERT_TRUE(mapped->ReadCString(0, &string));
+ EXPECT_EQ("0", string);
+ ASSERT_TRUE(mapped->ReadCString(1, &string));
+ EXPECT_EQ("", string);
+ ASSERT_TRUE(mapped->ReadCString(2, &string));
+ EXPECT_EQ("2", string);
+ ASSERT_TRUE(mapped->ReadCString(3, &string));
+ EXPECT_EQ("", string);
+ ASSERT_TRUE(mapped->ReadCString(4, &string));
+ EXPECT_EQ("45", string);
+ ASSERT_TRUE(mapped->ReadCString(5, &string));
+ EXPECT_EQ("5", string);
+ ASSERT_TRUE(mapped->ReadCString(6, &string));
+ EXPECT_EQ("", string);
+
+ // kTestBuffer’s NUL terminator was not read, so these will see an
+ // unterminated string and fail.
+ EXPECT_FALSE(mapped->ReadCString(7, &string));
+ EXPECT_FALSE(mapped->ReadCString(8, &string));
+ EXPECT_FALSE(mapped->ReadCString(9, &string));
+
+ // This is out of the range of what was read, so it will fail.
+ EXPECT_FALSE(mapped->ReadCString(10, &string));
+ EXPECT_FALSE(mapped->ReadCString(11, &string));
+
+ // Read it again, this time with a length long enough to include the NUL
+ // terminator.
+ ASSERT_TRUE((mapped = memory.ReadMapped(kTestAddress, 11)));
+
+ ASSERT_TRUE(mapped->ReadCString(6, &string));
+ EXPECT_EQ("", string);
+
+ // These should now succeed.
+ ASSERT_TRUE(mapped->ReadCString(7, &string));
+ EXPECT_EQ("789", string);
+ ASSERT_TRUE(mapped->ReadCString(8, &string));
+ EXPECT_EQ("89", string);
+ ASSERT_TRUE(mapped->ReadCString(9, &string));
+ EXPECT_EQ("9", string);
+ EXPECT_TRUE(mapped->ReadCString(10, &string));
+ EXPECT_EQ("", string);
+
+ // These are still out of range.
+ EXPECT_FALSE(mapped->ReadCString(11, &string));
+ EXPECT_FALSE(mapped->ReadCString(12, &string));
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/misc/clock.h b/chromium/third_party/crashpad/crashpad/util/misc/clock.h
new file mode 100644
index 00000000000..a07e12a6ee1
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/misc/clock.h
@@ -0,0 +1,52 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MISC_CLOCK_H_
+#define CRASHPAD_UTIL_MISC_CLOCK_H_
+
+#include <stdint.h>
+
+#include "build/build_config.h"
+
+namespace crashpad {
+
+//! \brief Returns the value of the system’s monotonic clock.
+//!
+//! The monotonic clock is a tick counter whose epoch is unspecified. It is a
+//! monotonically-increasing clock that cannot be set, and never jumps backwards
+//! on a running system. The monotonic clock may stop while the system is
+//! sleeping, and it may be reset when the system starts up. This clock is
+//! suitable for computing durations of events. Subject to the underlying
+//! clock’s resolution, successive calls to this function will result in a
+//! series of increasing values.
+//!
+//! \return The value of the system’s monotonic clock, in nanoseconds.
+uint64_t ClockMonotonicNanoseconds();
+
+#if !defined(OS_WIN) // Not implemented on Windows yet.
+
+//! \brief Sleeps for the specified duration.
+//!
+//! \param[in] nanoseconds The number of nanoseconds to sleep. The actual sleep
+//! may be slightly longer due to latencies and timer resolution.
+//!
+//! This function is resilient against the underlying `nanosleep()` system call
+//! being interrupted by a signal.
+void SleepNanoseconds(uint64_t nanoseconds);
+
+#endif
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MISC_CLOCK_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/misc/clock_mac.cc b/chromium/third_party/crashpad/crashpad/util/misc/clock_mac.cc
new file mode 100644
index 00000000000..bb52299efa9
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/misc/clock_mac.cc
@@ -0,0 +1,45 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/misc/clock.h"
+
+#include <mach/mach_time.h>
+
+#include "base/mac/mach_logging.h"
+
+namespace {
+
+mach_timebase_info_data_t* TimebaseInternal() {
+ mach_timebase_info_data_t* timebase_info = new mach_timebase_info_data_t;
+ kern_return_t kr = mach_timebase_info(timebase_info);
+ MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_timebase_info";
+ return timebase_info;
+}
+
+mach_timebase_info_data_t* Timebase() {
+ static mach_timebase_info_data_t* timebase_info = TimebaseInternal();
+ return timebase_info;
+}
+
+} // namespace
+
+namespace crashpad {
+
+uint64_t ClockMonotonicNanoseconds() {
+ uint64_t absolute_time = mach_absolute_time();
+ mach_timebase_info_data_t* timebase_info = Timebase();
+ return absolute_time * timebase_info->numer / timebase_info->denom;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/misc/clock_posix.cc b/chromium/third_party/crashpad/crashpad/util/misc/clock_posix.cc
new file mode 100644
index 00000000000..24171091db1
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/misc/clock_posix.cc
@@ -0,0 +1,51 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/misc/clock.h"
+
+#include <time.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+
+namespace {
+
+const uint64_t kNanosecondsPerSecond = 1E9;
+
+} // namespace
+
+namespace crashpad {
+
+#if !defined(OS_MACOSX)
+
+uint64_t ClockMonotonicNanoseconds() {
+ timespec now;
+ int rv = clock_gettime(CLOCK_MONOTONIC, &now);
+ DPCHECK(rv == 0) << "clock_gettime";
+
+ return now.tv_sec * kNanosecondsPerSecond + now.tv_nsec;
+}
+
+#endif
+
+void SleepNanoseconds(uint64_t nanoseconds) {
+ timespec sleep_time;
+ sleep_time.tv_sec = nanoseconds / kNanosecondsPerSecond;
+ sleep_time.tv_nsec = nanoseconds % kNanosecondsPerSecond;
+ int rv = HANDLE_EINTR(nanosleep(&sleep_time, &sleep_time));
+ DPCHECK(rv == 0) << "nanosleep";
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/misc/clock_test.cc b/chromium/third_party/crashpad/crashpad/util/misc/clock_test.cc
new file mode 100644
index 00000000000..82ca85fa765
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/misc/clock_test.cc
@@ -0,0 +1,98 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/misc/clock.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(Clock, ClockMonotonicNanoseconds) {
+ uint64_t start = ClockMonotonicNanoseconds();
+ EXPECT_GT(start, 0u);
+
+ uint64_t now = start;
+ for (size_t iteration = 0; iteration < 10; ++iteration) {
+ uint64_t last = now;
+ now = ClockMonotonicNanoseconds();
+
+ // Use EXPECT_GE instead of EXPECT_GT, because there are no guarantees about
+ // the clock’s resolution.
+ EXPECT_GE(now, last);
+ }
+
+#if !defined(OS_WIN) // No SleepNanoseconds implemented on Windows.
+ // SleepNanoseconds() should sleep for at least the value of the clock’s
+ // resolution, so the clock’s value should definitely increase after a sleep.
+ // EXPECT_GT can be used instead of EXPECT_GE after the sleep.
+ SleepNanoseconds(1);
+ now = ClockMonotonicNanoseconds();
+ EXPECT_GT(now, start);
+#endif // OS_WIN
+}
+
+#if !defined(OS_WIN) // No SleepNanoseconds implemented on Windows.
+
+void TestSleepNanoseconds(uint64_t nanoseconds) {
+ uint64_t start = ClockMonotonicNanoseconds();
+
+ SleepNanoseconds(nanoseconds);
+
+ uint64_t end = ClockMonotonicNanoseconds();
+ uint64_t diff = end - start;
+
+ // |nanoseconds| is the lower bound for the actual amount of time spent
+ // sleeping.
+ EXPECT_GE(diff, nanoseconds);
+
+ // It’s difficult to set an upper bound for the time spent sleeping, and
+ // attempting to do so results in a flaky test.
+}
+
+TEST(Clock, SleepNanoseconds) {
+ const uint64_t kTestData[] = {
+ 0,
+ 1,
+ static_cast<uint64_t>(1E3), // 1 microsecond
+ static_cast<uint64_t>(1E4), // 10 microseconds
+ static_cast<uint64_t>(1E5), // 100 microseconds
+ static_cast<uint64_t>(1E6), // 1 millisecond
+ static_cast<uint64_t>(1E7), // 10 milliseconds
+ static_cast<uint64_t>(2E7), // 20 milliseconds
+ static_cast<uint64_t>(5E7), // 50 milliseconds
+ };
+
+ for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ const uint64_t nanoseconds = kTestData[index];
+ SCOPED_TRACE(
+ base::StringPrintf("index %zu, nanoseconds %llu", index, nanoseconds));
+
+ TestSleepNanoseconds(nanoseconds);
+ }
+}
+
+#endif // OS_WIN
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/misc/clock_win.cc b/chromium/third_party/crashpad/crashpad/util/misc/clock_win.cc
new file mode 100644
index 00000000000..731e98d4ee2
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/misc/clock_win.cc
@@ -0,0 +1,48 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/misc/clock.h"
+
+#include <sys/types.h>
+#include <windows.h>
+
+namespace {
+
+LARGE_INTEGER QpcFrequencyInternal() {
+ LARGE_INTEGER frequency;
+ QueryPerformanceFrequency(&frequency);
+ return frequency;
+}
+
+int64_t QpcFrequency() {
+ static LARGE_INTEGER frequency = QpcFrequencyInternal();
+ return frequency.QuadPart;
+}
+
+} // namespace
+
+namespace crashpad {
+
+uint64_t ClockMonotonicNanoseconds() {
+ LARGE_INTEGER time;
+ QueryPerformanceCounter(&time);
+ int64_t frequency = QpcFrequency();
+ int64_t whole_seconds = time.QuadPart / frequency;
+ int64_t leftover_ticks = time.QuadPart / (whole_seconds * frequency);
+ const int64_t kNanosecondsPerSecond = static_cast<const int64_t>(1E9);
+ return (whole_seconds * kNanosecondsPerSecond) +
+ ((leftover_ticks * kNanosecondsPerSecond) / frequency);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/misc/initialization_state.h b/chromium/third_party/crashpad/crashpad/util/misc/initialization_state.h
new file mode 100644
index 00000000000..b0496e5c131
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/misc/initialization_state.h
@@ -0,0 +1,100 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MISC_INITIALIZATION_INITIALIZATION_STATE_H_
+#define CRASHPAD_UTIL_MISC_INITIALIZATION_INITIALIZATION_STATE_H_
+
+#include <stdint.h>
+
+#include "base/basictypes.h"
+
+namespace crashpad {
+
+//! \brief Tracks whether data are initialized.
+//!
+//! Objects of this type track whether the data they’re guarding are
+//! initialized. The three possible states are uninitialized (the initial
+//! state), initializing, and valid. As the guarded data are initialized, an
+//! InitializationState object will normally transition through these three
+//! states. A fourth state corresponds to the destruction of objects of this
+//! type, making it less likely that a use-after-free of an InitializationState
+//! object will appear in the valid state.
+//!
+//! If the only purpose for tracking the initialization state of guarded data is
+//! to DCHECK when the object is in an unexpected state, use
+//! InitializationStateDcheck instead.
+class InitializationState {
+ public:
+ //! \brief The object’s state.
+ enum State : uint8_t {
+ //! \brief The object has not yet been initialized.
+ kStateUninitialized = 0,
+
+ //! \brief The object is being initialized.
+ //!
+ //! This state protects against attempted reinitializaton of
+ //! partially-initialized objects whose initial initialization attempt
+ //! failed. This state is to be used while objects are initializing, but are
+ //! not yet fully initialized.
+ kStateInvalid,
+
+ //! \brief The object has been initialized.
+ kStateValid,
+
+ //! \brief The object has been destroyed.
+ kStateDestroyed,
+ };
+
+ InitializationState() : state_(kStateUninitialized) {}
+ ~InitializationState() { state_ = kStateDestroyed; }
+
+ //! \brief Returns `true` if the object’s state is #kStateUninitialized and it
+ //! is safe to begin initializing it.
+ bool is_uninitialized() const { return state_ == kStateUninitialized; }
+
+ //! \brief Sets the object’s state to #kStateInvalid, marking initialization
+ //! as being in process.
+ void set_invalid() { state_ = kStateInvalid; }
+
+ //! \brief Sets the object’s state to #kStateValid, marking it initialized.
+ void set_valid() { state_ = kStateValid; }
+
+ //! \brief Returns `true` if the the object’s state is #kStateValid and it has
+ //! been fully initialized and may be used.
+ bool is_valid() const { return state_ == kStateValid; }
+
+ protected:
+ //! \brief Returns the object’s state.
+ //!
+ //! Consumers of this class should use an is_state_*() method instead.
+ State state() const { return state_; }
+
+ //! \brief Sets the object’s state.
+ //!
+ //! Consumers of this class should use a set_state_*() method instead.
+ void set_state(State state) { state_ = state; }
+
+ private:
+ // state_ is volatile to ensure that it’ll be set by the destructor when it
+ // runs. Otherwise, optimizations might prevent it from ever being set to
+ // kStateDestroyed, limiting this class’ ability to catch use-after-free
+ // errors.
+ volatile State state_;
+
+ DISALLOW_COPY_AND_ASSIGN(InitializationState);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MISC_INITIALIZATION_INITIALIZATION_STATE_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/misc/initialization_state_dcheck.cc b/chromium/third_party/crashpad/crashpad/util/misc/initialization_state_dcheck.cc
new file mode 100644
index 00000000000..ac584dc6615
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/misc/initialization_state_dcheck.cc
@@ -0,0 +1,41 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+
+#if DCHECK_IS_ON
+
+InitializationStateDcheck::State InitializationStateDcheck::SetInitializing() {
+ State old_state = state();
+ if (old_state == kStateUninitialized) {
+ set_invalid();
+ }
+
+ return old_state;
+}
+
+InitializationStateDcheck::State InitializationStateDcheck::SetValid() {
+ State old_state = state();
+ if (old_state == kStateInvalid) {
+ set_valid();
+ }
+
+ return old_state;
+}
+
+#endif
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/misc/initialization_state_dcheck.h b/chromium/third_party/crashpad/crashpad/util/misc/initialization_state_dcheck.h
new file mode 100644
index 00000000000..790a3d8cf75
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/misc/initialization_state_dcheck.h
@@ -0,0 +1,188 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MISC_INITIALIZATION_INITIALIZATION_STATE_DCHECK_H_
+#define CRASHPAD_UTIL_MISC_INITIALIZATION_INITIALIZATION_STATE_DCHECK_H_
+
+//! \file
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "util/misc/initialization_state.h"
+
+namespace crashpad {
+
+#if DCHECK_IS_ON || DOXYGEN
+
+//! \brief Tracks whether data are initialized, triggering a DCHECK assertion
+//! on an invalid data access.
+//!
+//! Put an InitializationStateDcheck member into a class to help DCHECK that
+//! it’s in the right states at the right times. This is useful for classes with
+//! Initialize() methods. The chief advantage of InitializationStateDcheck over
+//! having a member variable to track state is that when the only use of the
+//! variable is to DCHECK, it wastes space (in memory and executable code) in
+//! non-DCHECK builds unless the code is also peppered with ugly #ifdefs.
+//!
+//! This implementation concentrates the ugly #ifdefs in one location.
+//!
+//! Usage:
+//!
+//! \code
+//! class Class {
+//! public:
+//! Class() : initialized_() {}
+//!
+//! void Initialize() {
+//! INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+//! // Perform initialization.
+//! INITIALIZATION_STATE_SET_VALID(initialized_);
+//! }
+//!
+//! void DoSomething() {
+//! INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+//! // Do something.
+//! }
+//!
+//! private:
+//! InitializationStateDcheck initialized_;
+//! };
+//! \endcode
+class InitializationStateDcheck : public InitializationState {
+ public:
+ InitializationStateDcheck() : InitializationState() {}
+
+ //! \brief Returns the object’s state.
+ //!
+ //! Consumers of this class should not call this method. Use the
+ //! INITIALIZATION_STATE_SET_INITIALIZING(), INITIALIZATION_STATE_SET_VALID(),
+ //! and INITIALIZATION_STATE_DCHECK_VALID() macros instead.
+ //
+ // The superclass’ state() accessor is protected, but it needs to be exposed
+ // to consumers of this class for the macros below to work properly. The
+ // macros prefer access to the unerlying state value over a simple boolean
+ // because with access to the state value, DCHECK_EQ can be used, which, when
+ // tripped, prints both the expected and observed values. This can aid
+ // troubleshooting.
+ State state() const { return InitializationState::state(); }
+
+ //! \brief Marks an uninitialized object as initializing.
+ //!
+ //! If the object is in the #kStateUninitialized state, changes its state to
+ //! #kStateInvalid (initializing) and returns the previous
+ //! (#kStateUninitialized) state. Otherwise, returns the object’s current
+ //! state.
+ //!
+ //! Consumers of this class should not call this method. Use the
+ //! INITIALIZATION_STATE_SET_INITIALIZING() macro instead.
+ State SetInitializing();
+
+ //! \brief Marks an initializing object as valid.
+ //!
+ //! If the object is in the #kStateInvalid (initializing) state, changes its
+ //! state to #kStateValid and returns the previous (#kStateInvalid) state.
+ //! Otherwise, returns the object’s current state.
+ //!
+ //! Consumers of this class should not call this method. Use the
+ //! INITIALIZATION_STATE_SET_VALID() macro instead.
+ State SetValid();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(InitializationStateDcheck);
+};
+
+// Using macros enables the non-DCHECK no-op implementation below to be more
+// compact and less intrusive. These are macros instead of methods that call
+// DCHECK to enable the DCHECK failure message to point to the correct file and
+// line number, and to allow additional messages to be streamed on failure with
+// the << operator.
+
+//! \brief Checks that a crashpad::InitializationStateDcheck object is in the
+//! crashpad::InitializationState::kStateUninitialized state, and changes
+//! its state to initializing
+//! (crashpad::InitializationState::kStateInvalid).
+//!
+//! If the object is not in the correct state, a DCHECK assertion is triggered
+//! and the object’s state remains unchanged.
+//!
+//! \param[in] initialization_state_dcheck A crashpad::InitializationStateDcheck
+//! object.
+//!
+//! \sa crashpad::InitializationStateDcheck
+#define INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck) \
+ DCHECK_EQ((initialization_state_dcheck).SetInitializing(), \
+ (initialization_state_dcheck).kStateUninitialized)
+
+//! \brief Checks that a crashpad::InitializationStateDcheck object is in the
+//! initializing (crashpad::InitializationState::kStateInvalid) state, and
+//! changes its state to crashpad::InitializationState::kStateValid.
+//!
+//! If the object is not in the correct state, a DCHECK assertion is triggered
+//! and the object’s state remains unchanged.
+//!
+//! \param[in] initialization_state_dcheck A crashpad::InitializationStateDcheck
+//! object.
+//!
+//! \sa crashpad::InitializationStateDcheck
+#define INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck) \
+ DCHECK_EQ((initialization_state_dcheck).SetValid(), \
+ (initialization_state_dcheck).kStateInvalid)
+
+//! \brief Checks that a crashpad::InitializationStateDcheck object is in the
+//! crashpad::InitializationState::kStateValid state.
+//!
+//! If the object is not in the correct state, a DCHECK assertion is triggered.
+//!
+//! \param[in] initialization_state_dcheck A crashpad::InitializationStateDcheck
+//! object.
+//!
+//! \sa crashpad::InitializationStateDcheck
+#define INITIALIZATION_STATE_DCHECK_VALID(initialization_state_dcheck) \
+ DCHECK_EQ((initialization_state_dcheck).state(), \
+ (initialization_state_dcheck).kStateValid)
+
+#else
+
+#if defined(COMPILER_MSVC)
+// bool[0] (below) is not accepted by MSVC.
+struct InitializationStateDcheck {
+};
+#else
+// Since this is to be used as a DCHECK (for debugging), it should be
+// non-intrusive in non-DCHECK (non-debug, release) builds. An empty struct
+// would still have a nonzero size (rationale:
+// http://www.stroustrup.com/bs_faq2.html#sizeof-empty). Zero-length arrays are
+// technically invalid according to the standard, but clang and g++ accept them
+// without complaint even with warnings turned up. They take up no space at all,
+// and they can be “initialized” with the same () syntax used to initialize
+// objects of the DCHECK_IS_ON InitializationStateDcheck class above.
+using InitializationStateDcheck = bool[0];
+#endif // COMPILER_MSVC
+
+// Avoid triggering warnings by repurposing these macros when DCHECKs are
+// disabled.
+#define INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck) \
+ ALLOW_UNUSED_LOCAL(initialization_state_dcheck)
+#define INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck) \
+ ALLOW_UNUSED_LOCAL(initialization_state_dcheck)
+#define INITIALIZATION_STATE_DCHECK_VALID(initialization_state_dcheck) \
+ ALLOW_UNUSED_LOCAL(initialization_state_dcheck)
+
+#endif
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MISC_INITIALIZATION_INITIALIZATION_STATE_DCHECK_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/misc/initialization_state_dcheck_test.cc b/chromium/third_party/crashpad/crashpad/util/misc/initialization_state_dcheck_test.cc
new file mode 100644
index 00000000000..7c68255eed1
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/misc/initialization_state_dcheck_test.cc
@@ -0,0 +1,135 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/misc/initialization_state_dcheck.h"
+
+#include "base/logging.h"
+#include "gtest/gtest.h"
+#include "test/gtest_death_check.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(InitializationStateDcheck, InitializationStateDcheck) {
+ InitializationStateDcheck initialization_state_dcheck;
+ INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck);
+ INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck);
+ INITIALIZATION_STATE_DCHECK_VALID(initialization_state_dcheck);
+}
+
+#if DCHECK_IS_ON
+
+// InitializationStateDcheck only DCHECKs, so the death tests can only run
+// when DCHECKs are enabled.
+
+TEST(InitializationStateDcheckDeathTest, Uninitialized_NotInvalid) {
+ // This tests that an attempt to set an uninitialized object as valid without
+ // transitioning through the initializing (invalid) state fails.
+ InitializationStateDcheck initialization_state_dcheck;
+ ASSERT_DEATH_CHECK(
+ INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck),
+ "kStateInvalid");
+}
+
+TEST(InitializationStateDcheckDeathTest, Uninitialized_NotValid) {
+ // This tests that an attempt to use an uninitialized object as though it
+ // were valid fails.
+ InitializationStateDcheck initialization_state_dcheck;
+ ASSERT_DEATH_CHECK(
+ INITIALIZATION_STATE_DCHECK_VALID(initialization_state_dcheck),
+ "kStateValid");
+}
+
+TEST(InitializationStateDcheckDeathTest, Invalid_NotUninitialized) {
+ // This tests that an attempt to begin initializing an object on which
+ // initialization was already attempted fails.
+ InitializationStateDcheck initialization_state_dcheck;
+ INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck);
+ ASSERT_DEATH_CHECK(
+ INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck),
+ "kStateUninitialized");
+}
+
+TEST(InitializationStateDcheckDeathTest, Invalid_NotValid) {
+ // This tests that an attempt to use an initializing object as though it
+ // were valid fails.
+ InitializationStateDcheck initialization_state_dcheck;
+ INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck);
+ ASSERT_DEATH_CHECK(
+ INITIALIZATION_STATE_DCHECK_VALID(initialization_state_dcheck),
+ "kStateValid");
+}
+
+TEST(InitializationStateDcheckDeathTest, Valid_NotUninitialized) {
+ // This tests that an attempt to begin initializing an object that has already
+ // been initialized fails.
+ InitializationStateDcheck initialization_state_dcheck;
+ INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck);
+ INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck);
+ ASSERT_DEATH_CHECK(
+ INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck),
+ "kStateUninitialized");
+}
+
+TEST(InitializationStateDcheckDeathTest, Valid_NotInvalid) {
+ // This tests that an attempt to set a valid object as valid a second time
+ // fails.
+ InitializationStateDcheck initialization_state_dcheck;
+ INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck);
+ INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck);
+ ASSERT_DEATH_CHECK(
+ INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck),
+ "kStateInvalid");
+}
+
+TEST(InitializationStateDcheckDeathTest, Destroyed_NotUninitialized) {
+ // This tests that an attempt to reinitialize a destroyed object fails. See
+ // the InitializationState.InitializationState test for an explanation of this
+ // use-after-free test.
+ InitializationStateDcheck* initialization_state_dcheck_pointer;
+ {
+ InitializationStateDcheck initialization_state_dcheck;
+ initialization_state_dcheck_pointer = &initialization_state_dcheck;
+ INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck);
+ INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck);
+ INITIALIZATION_STATE_DCHECK_VALID(initialization_state_dcheck);
+ }
+ ASSERT_DEATH_CHECK(INITIALIZATION_STATE_SET_INITIALIZING(
+ *initialization_state_dcheck_pointer),
+ "kStateUninitialized");
+}
+
+TEST(InitializationStateDcheckDeathTest, Destroyed_NotValid) {
+ // This tests that an attempt to use a destroyed object fails. See the
+ // InitializationState.InitializationState test for an explanation of this
+ // use-after-free test.
+ InitializationStateDcheck* initialization_state_dcheck_pointer;
+ {
+ InitializationStateDcheck initialization_state_dcheck;
+ initialization_state_dcheck_pointer = &initialization_state_dcheck;
+ INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck);
+ INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck);
+ INITIALIZATION_STATE_DCHECK_VALID(initialization_state_dcheck);
+ }
+ ASSERT_DEATH_CHECK(
+ INITIALIZATION_STATE_DCHECK_VALID(*initialization_state_dcheck_pointer),
+ "kStateValid");
+}
+
+#endif
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/misc/initialization_state_test.cc b/chromium/third_party/crashpad/crashpad/util/misc/initialization_state_test.cc
new file mode 100644
index 00000000000..d427a140dd7
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/misc/initialization_state_test.cc
@@ -0,0 +1,58 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/misc/initialization_state.h"
+
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(InitializationState, InitializationState) {
+ InitializationState* initialization_state_pointer;
+ {
+ InitializationState initialization_state;
+ initialization_state_pointer = &initialization_state;
+
+ EXPECT_TRUE(initialization_state.is_uninitialized());
+ EXPECT_FALSE(initialization_state.is_valid());
+
+ initialization_state.set_invalid();
+
+ EXPECT_FALSE(initialization_state.is_uninitialized());
+ EXPECT_FALSE(initialization_state.is_valid());
+
+ initialization_state.set_valid();
+
+ EXPECT_FALSE(initialization_state.is_uninitialized());
+ EXPECT_TRUE(initialization_state.is_valid());
+ }
+
+ // initialization_state_pointer points to something that no longer exists.
+ // This portion of the test is intended to check that after an
+ // InitializationState object goes out of scope, it will not be considered
+ // valid on a use-after-free, assuming that nothing else was written to its
+ // former home in memory.
+ //
+ // This portion of the test is technically not valid C++, but it exists to
+ // test that the behavior is as desired when other code uses the language
+ // improperly.
+ EXPECT_FALSE(initialization_state_pointer->is_uninitialized());
+ EXPECT_FALSE(initialization_state_pointer->is_valid());
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/misc/scoped_forbid_return.cc b/chromium/third_party/crashpad/crashpad/util/misc/scoped_forbid_return.cc
new file mode 100644
index 00000000000..f8de1da49db
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/misc/scoped_forbid_return.cc
@@ -0,0 +1,27 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/misc/scoped_forbid_return.h"
+
+#include "base/logging.h"
+
+namespace crashpad {
+
+ScopedForbidReturn::~ScopedForbidReturn() {
+ if (armed_) {
+ LOG(FATAL) << "attempt to exit scope forbidden";
+ }
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/misc/scoped_forbid_return.h b/chromium/third_party/crashpad/crashpad/util/misc/scoped_forbid_return.h
new file mode 100644
index 00000000000..8705be7ad24
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/misc/scoped_forbid_return.h
@@ -0,0 +1,54 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MISC_SCOPED_FORBID_RETURN_H_
+#define CRASHPAD_UTIL_MISC_SCOPED_FORBID_RETURN_H_
+
+#include "base/basictypes.h"
+
+namespace crashpad {
+
+//! \brief Asserts that a scope must not be exited while unsafe.
+//!
+//! An object of this class has two states: armed and disarmed. A disarmed
+//! object is a harmless no-op. An armed object will abort execution upon
+//! destruction. Newly-constructed objects are armed by default.
+//!
+//! These objects may be used to assert that a scope not be exited while it is
+//! unsafe to do so. If it ever becomes safe to leave such a scope, an object
+//! can be disarmed.
+class ScopedForbidReturn {
+ public:
+ ScopedForbidReturn() : armed_(true) {}
+ ~ScopedForbidReturn();
+
+ //! \brief Arms the object so that it will abort execution when destroyed.
+ //!
+ //! The most recent call to Arm() or Disarm() sets the state of the object.
+ void Arm() { armed_ = true; }
+
+ //! \brief Arms the object so that it will abort execution when destroyed.
+ //!
+ //! The most recent call to Arm() or Disarm() sets the state of the object.
+ void Disarm() { armed_ = false; }
+
+ private:
+ bool armed_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedForbidReturn);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MISC_SCOPED_FORBID_RETURN_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/misc/scoped_forbid_return_test.cc b/chromium/third_party/crashpad/crashpad/util/misc/scoped_forbid_return_test.cc
new file mode 100644
index 00000000000..78787d4918a
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/misc/scoped_forbid_return_test.cc
@@ -0,0 +1,68 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/misc/scoped_forbid_return.h"
+
+#include "base/compiler_specific.h"
+#include "gtest/gtest.h"
+#include "test/gtest_death_check.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+enum ForbidReturnType {
+ kForbidReturnDefault = 0,
+ kForbidReturnArmed,
+ kForbidReturnDisarmed,
+};
+
+void ScopedForbidReturnHelper(ForbidReturnType type) {
+ ScopedForbidReturn forbid_return;
+
+ switch (type) {
+ case kForbidReturnDefault:
+ break;
+ case kForbidReturnArmed:
+ forbid_return.Arm();
+ break;
+ case kForbidReturnDisarmed:
+ forbid_return.Disarm();
+ break;
+ }
+}
+
+const char kForbiddenMessage[] = "attempt to exit scope forbidden";
+
+TEST(ScopedForbidReturnDeathTest, Default) {
+ // kForbiddenMessage may appear to be unused if ASSERT_DEATH_CHECK() throws it
+ // away.
+ ALLOW_UNUSED_LOCAL(kForbiddenMessage);
+
+ ASSERT_DEATH_CHECK(ScopedForbidReturnHelper(kForbidReturnDefault),
+ kForbiddenMessage);
+}
+
+TEST(ScopedForbidReturnDeathTest, Armed) {
+ ASSERT_DEATH_CHECK(ScopedForbidReturnHelper(kForbidReturnArmed),
+ kForbiddenMessage);
+}
+
+TEST(ScopedForbidReturn, Disarmed) {
+ ScopedForbidReturnHelper(kForbidReturnDisarmed);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/misc/symbolic_constants_common.h b/chromium/third_party/crashpad/crashpad/util/misc/symbolic_constants_common.h
new file mode 100644
index 00000000000..ea2495aa009
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/misc/symbolic_constants_common.h
@@ -0,0 +1,132 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MISC_SYMBOLIC_CONSTANTS_COMMON_H_
+#define CRASHPAD_UTIL_MISC_SYMBOLIC_CONSTANTS_COMMON_H_
+
+//! \file
+//!
+//! \anchor symbolic_constant_terminology
+//! Symbolic constant terminology
+//! =============================
+//! <dl>
+//! <dt>Family</dt>
+//! <dd>A group of related symbolic constants. Typically, within a single
+//! family, one function will be used to transform a numeric value to a
+//! string equivalent, and another will perform the inverse operation.
+//! Families include POSIX signals and Mach exception masks.</dd>
+//! <dt>Full name</dt>
+//! <dd>The normal symbolic name used for a constant. For example, in the
+//! family of POSIX signals, the strings `"SIGHUP"` and `"SIGSEGV"` are
+//! full names.</dd>
+//! <dt>Short name</dt>
+//! <dd>An abbreviated form of symbolic name used for a constant. Short names
+//! vary between families, but are commonly constructed by removing a
+//! common prefix from full names. For example, in the family of POSIX
+//! signals, the prefix is `SIG`, and short names include `"HUP"` and
+//! `"SEGV"`.</dd>
+//! <dt>Numeric string</dt>
+//! <dd>A string that does not contain a full or short name, but contains a
+//! numeric value that can be interpreted as a symbolic constant. For
+//! example, in the family of POSIX signals, `SIGKILL` generally has value
+//! `9`, so the numeric string `"9"` would be interpreted equivalently to
+//! `"SIGKILL"`.</dd>
+//! </dl>
+
+namespace crashpad {
+
+//! \brief Options for various `*ToString` functions in `symbolic_constants_*`
+//! files.
+//!
+//! \sa \ref symbolic_constant_terminology "Symbolic constant terminology"
+enum SymbolicConstantToStringOptionBits {
+ //! \brief Return the full name for a given constant.
+ //!
+ //! \attention API consumers should provide this value when desired, but
+ //! should provide only one of kUseFullName and ::kUseShortName. Because
+ //! kUseFullName is valueless, implementers should check for the absence
+ //! of ::kUseShortName instead.
+ kUseFullName = 0 << 0,
+
+ //! \brief Return the short name for a given constant.
+ kUseShortName = 1 << 0,
+
+ //! \brief If no symbolic name is known for a given constant, return an empty
+ //! string.
+ //!
+ //! \attention API consumers should provide this value when desired, but
+ //! should provide only one of kUnknownIsEmpty and ::kUnknownIsNumeric.
+ //! Because kUnknownIsEmpty is valueless, implementers should check for
+ //! the absence of ::kUnknownIsNumeric instead.
+ kUnknownIsEmpty = 0 << 1,
+
+ //! \brief If no symbolic name is known for a given constant, return a numeric
+ //! string.
+ //!
+ //! The numeric format used will vary by family, but will be appropriate to
+ //! the family. Families whose values are typically constructed as bitfields
+ //! will generally use a hexadecimal format, and other families will generally
+ //! use a signed or unsigned decimal format.
+ kUnknownIsNumeric = 1 << 1,
+
+ //! \brief Use `|` to combine values in a bitfield.
+ //!
+ //! For families whose values may be constructed as bitfields, allow
+ //! conversion to strings containing multiple individual components treated as
+ //! being combined by a bitwise “or” operation. An example family of constants
+ //! that behaves this way is the suite of Mach exception masks. For constants
+ //! that are not constructed as bitfields, or constants that are only
+ //! partially constructed as bitfields, this option has no effect.
+ kUseOr = 1 << 2,
+};
+
+//! \brief A bitfield containing values of #SymbolicConstantToStringOptionBits.
+using SymbolicConstantToStringOptions = unsigned int;
+
+//! \brief Options for various `StringTo*` functions in `symbolic_constants_*`
+//! files.
+//!
+//! Not every `StringTo*` function will implement each of these options. See
+//! function-specific documentation for details.
+//!
+//! \sa \ref symbolic_constant_terminology "Symbolic constant terminology"
+enum StringToSymbolicConstantOptionBits {
+ //! \brief Allow conversion from a string containing a symbolic constant by
+ //! its full name.
+ kAllowFullName = 1 << 0,
+
+ //! \brief Allow conversion from a string containing a symbolic constant by
+ //! its short name.
+ kAllowShortName = 1 << 1,
+
+ //! \brief Allow conversion from a numeric string.
+ kAllowNumber = 1 << 2,
+
+ //! \brief Allow `|` to combine values in a bitfield.
+ //!
+ //! For families whose values may be constructed as bitfields, allow
+ //! conversion of strings containing multiple individual components treated as
+ //! being combined by a bitwise “or” operation. An example family of constants
+ //! that behaves this way is the suite of Mach exception masks. For constants
+ //! that are not constructed as bitfields, or constants that are only
+ //! partially constructed as bitfields, this option has no effect.
+ kAllowOr = 1 << 3,
+};
+
+//! \brief A bitfield containing values of #StringToSymbolicConstantOptionBits.
+using StringToSymbolicConstantOptions = unsigned int;
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MISC_SYMBOLIC_CONSTANTS_COMMON_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/misc/tri_state.h b/chromium/third_party/crashpad/crashpad/util/misc/tri_state.h
new file mode 100644
index 00000000000..be557214886
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/misc/tri_state.h
@@ -0,0 +1,41 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MISC_TRI_STATE_H_
+#define CRASHPAD_UTIL_MISC_TRI_STATE_H_
+
+#include <stdint.h>
+
+namespace crashpad {
+
+//! \brief A tri-state value that can be unset, on, or off.
+enum class TriState : uint8_t {
+ //! \brief The value has not explicitly been set.
+ //!
+ //! To allow a zero-initialized value to have this behavior, this must have
+ //! the value `0`.
+ kUnset = 0,
+
+ //! \brief The value has explicitly been set to on, or a behavior has
+ //! explicitly been enabled.
+ kEnabled,
+
+ //! \brief The value has explicitly been set to off, or a behavior has
+ //! explicitly been disabled.
+ kDisabled,
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MISC_TRI_STATE_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/misc/uuid.cc b/chromium/third_party/crashpad/crashpad/util/misc/uuid.cc
new file mode 100644
index 00000000000..ca7b22b1fff
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/misc/uuid.cc
@@ -0,0 +1,150 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#if !defined(__STDC_FORMAT_MACROS)
+#define __STDC_FORMAT_MACROS
+#endif
+
+#include "util/misc/uuid.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/sys_byteorder.h"
+#include "util/stdlib/cxx.h"
+
+#if defined(OS_MACOSX)
+#include <uuid/uuid.h>
+#endif // OS_MACOSX
+
+#if CXX_LIBRARY_VERSION >= 2011
+#include <type_traits>
+#endif
+
+namespace crashpad {
+
+static_assert(sizeof(UUID) == 16, "UUID must be 16 bytes");
+
+#if CXX_LIBRARY_VERSION >= 2011
+static_assert(std::is_standard_layout<UUID>::value,
+ "UUID must be standard layout");
+#endif
+
+UUID::UUID() : data_1(0), data_2(0), data_3(0), data_4(), data_5() {
+}
+
+UUID::UUID(InitializeWithNewTag) {
+ CHECK(InitializeWithNew());
+}
+
+UUID::UUID(const uint8_t* bytes) {
+ InitializeFromBytes(bytes);
+}
+
+bool UUID::operator==(const UUID& that) const {
+ return memcmp(this, &that, sizeof(UUID)) == 0;
+}
+
+void UUID::InitializeFromBytes(const uint8_t* bytes) {
+ memcpy(this, bytes, sizeof(*this));
+ data_1 = base::NetToHost32(data_1);
+ data_2 = base::NetToHost16(data_2);
+ data_3 = base::NetToHost16(data_3);
+}
+
+bool UUID::InitializeFromString(const base::StringPiece& string) {
+ if (string.length() != 36)
+ return false;
+
+ UUID temp;
+ const char kScanFormat[] =
+ "%08" SCNx32 "-%04" SCNx16 "-%04" SCNx16
+ "-%02" SCNx8 "%02" SCNx8
+ "-%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8;
+ int rv = sscanf(string.data(),
+ kScanFormat,
+ &temp.data_1,
+ &temp.data_2,
+ &temp.data_3,
+ &temp.data_4[0],
+ &temp.data_4[1],
+ &temp.data_5[0],
+ &temp.data_5[1],
+ &temp.data_5[2],
+ &temp.data_5[3],
+ &temp.data_5[4],
+ &temp.data_5[5]);
+ if (rv != 11)
+ return false;
+
+ *this = temp;
+ return true;
+}
+
+bool UUID::InitializeWithNew() {
+#if defined(OS_MACOSX)
+ uuid_t uuid;
+ uuid_generate(uuid);
+ InitializeFromBytes(uuid);
+ return true;
+#elif defined(OS_WIN)
+ ::UUID system_uuid;
+ if (UuidCreate(&system_uuid) != RPC_S_OK) {
+ LOG(ERROR) << "UuidCreate";
+ return false;
+ }
+ InitializeFromSystemUUID(&system_uuid);
+ return true;
+#else
+#error Port.
+#endif // OS_MACOSX
+}
+
+#if defined(OS_WIN)
+void UUID::InitializeFromSystemUUID(const ::UUID* system_uuid) {
+ static_assert(sizeof(::UUID) == sizeof(UUID),
+ "unexpected system uuid size");
+ static_assert(offsetof(::UUID, Data1) == offsetof(UUID, data_1),
+ "unexpected system uuid layout");
+ memcpy(this, system_uuid, sizeof(::UUID));
+}
+#endif // OS_WIN
+
+std::string UUID::ToString() const {
+ return base::StringPrintf("%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ data_1,
+ data_2,
+ data_3,
+ data_4[0],
+ data_4[1],
+ data_5[0],
+ data_5[1],
+ data_5[2],
+ data_5[3],
+ data_5[4],
+ data_5[5]);
+}
+
+#if defined(OS_WIN)
+base::string16 UUID::ToString16() const {
+ return base::UTF8ToUTF16(ToString());
+}
+#endif // OS_WIN
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/misc/uuid.h b/chromium/third_party/crashpad/crashpad/util/misc/uuid.h
new file mode 100644
index 00000000000..f25aa652f64
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/misc/uuid.h
@@ -0,0 +1,114 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MISC_UUID_H_
+#define CRASHPAD_UTIL_MISC_UUID_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <rpc.h>
+#endif
+
+namespace crashpad {
+
+//! \brief A universally unique identifier (%UUID).
+//!
+//! An alternate term for %UUID is “globally unique identifier” (GUID), used
+//! primarily by Microsoft.
+//!
+//! A %UUID is a unique 128-bit number specified by RFC 4122.
+//!
+//! This is a standard-layout structure.
+struct UUID {
+ //! \brief Initializes the %UUID to zero.
+ UUID();
+
+ //! \brief Tag to pass to constructor to indicate it should initialize with
+ //! generated data.
+ struct InitializeWithNewTag {};
+
+ //! \brief Initializes the %UUID using a standard system facility to generate
+ //! the value.
+ //!
+ //! CHECKs on failure with a message logged.
+ explicit UUID(InitializeWithNewTag);
+
+ //! \copydoc InitializeFromBytes()
+ explicit UUID(const uint8_t* bytes);
+
+ bool operator==(const UUID& that) const;
+ bool operator!=(const UUID& that) const { return !operator==(that); }
+
+ //! \brief Initializes the %UUID from a sequence of bytes.
+ //!
+ //! \a bytes is taken as a %UUID laid out in big-endian format in memory. On
+ //! little-endian machines, appropriate byte-swapping will be performed to
+ //! initialize an object’s data members.
+ //!
+ //! \param[in] bytes A buffer of exactly 16 bytes that will be assigned to the
+ //! %UUID.
+ void InitializeFromBytes(const uint8_t* bytes);
+
+ //! \brief Initializes the %UUID from a RFC 4122 §3 formatted string.
+ //!
+ //! \param[in] string A string of the form
+ //! `"00112233-4455-6677-8899-aabbccddeeff"`.
+ //!
+ //! \return `true` if the string was formatted correctly and the object has
+ //! been initialized with the data. `false` if the string could not be
+ //! parsed, with the object state untouched.
+ bool InitializeFromString(const base::StringPiece& string);
+
+ //! \brief Initializes the %UUID using a standard system facility to generate
+ //! the value.
+ //!
+ //! \return `true` if the %UUID was initialized correctly, `false` otherwise
+ //! with a message logged.
+ bool InitializeWithNew();
+
+#if defined(OS_WIN) || DOXYGEN
+ //! \brief Initializes the %UUID from a system `UUID` or `GUID` structure.
+ //!
+ //! \param[in] system_uuid A system `UUID` or `GUID` structure.
+ void InitializeFromSystemUUID(const ::UUID* system_uuid);
+#endif // OS_WIN
+
+ //! \brief Formats the %UUID per RFC 4122 §3.
+ //!
+ //! \return A string of the form `"00112233-4455-6677-8899-aabbccddeeff"`.
+ std::string ToString() const;
+
+#if defined(OS_WIN) || DOXYGEN
+ //! \brief The same as ToString, but returned as a string16.
+ base::string16 ToString16() const;
+#endif // OS_WIN
+
+ // These fields are laid out according to RFC 4122 §4.1.2.
+ uint32_t data_1;
+ uint16_t data_2;
+ uint16_t data_3;
+ uint8_t data_4[2];
+ uint8_t data_5[6];
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MISC_UUID_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/misc/uuid_test.cc b/chromium/third_party/crashpad/crashpad/util/misc/uuid_test.cc
new file mode 100644
index 00000000000..0dc6b4257f5
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/misc/uuid_test.cc
@@ -0,0 +1,240 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/misc/uuid.h"
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/format_macros.h"
+#include "base/scoped_generic.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(UUID, UUID) {
+ UUID uuid_zero;
+ EXPECT_EQ(0u, uuid_zero.data_1);
+ EXPECT_EQ(0u, uuid_zero.data_2);
+ EXPECT_EQ(0u, uuid_zero.data_3);
+ EXPECT_EQ(0u, uuid_zero.data_4[0]);
+ EXPECT_EQ(0u, uuid_zero.data_4[1]);
+ EXPECT_EQ(0u, uuid_zero.data_5[0]);
+ EXPECT_EQ(0u, uuid_zero.data_5[1]);
+ EXPECT_EQ(0u, uuid_zero.data_5[2]);
+ EXPECT_EQ(0u, uuid_zero.data_5[3]);
+ EXPECT_EQ(0u, uuid_zero.data_5[4]);
+ EXPECT_EQ(0u, uuid_zero.data_5[5]);
+ EXPECT_EQ("00000000-0000-0000-0000-000000000000", uuid_zero.ToString());
+
+ const uint8_t kBytes[16] = {0x00,
+ 0x01,
+ 0x02,
+ 0x03,
+ 0x04,
+ 0x05,
+ 0x06,
+ 0x07,
+ 0x08,
+ 0x09,
+ 0x0a,
+ 0x0b,
+ 0x0c,
+ 0x0d,
+ 0x0e,
+ 0x0f};
+ UUID uuid(kBytes);
+ EXPECT_EQ(0x00010203u, uuid.data_1);
+ EXPECT_EQ(0x0405u, uuid.data_2);
+ EXPECT_EQ(0x0607u, uuid.data_3);
+ EXPECT_EQ(0x08u, uuid.data_4[0]);
+ EXPECT_EQ(0x09u, uuid.data_4[1]);
+ EXPECT_EQ(0x0au, uuid.data_5[0]);
+ EXPECT_EQ(0x0bu, uuid.data_5[1]);
+ EXPECT_EQ(0x0cu, uuid.data_5[2]);
+ EXPECT_EQ(0x0du, uuid.data_5[3]);
+ EXPECT_EQ(0x0eu, uuid.data_5[4]);
+ EXPECT_EQ(0x0fu, uuid.data_5[5]);
+ EXPECT_EQ("00010203-0405-0607-0809-0a0b0c0d0e0f", uuid.ToString());
+
+ // Test both operator== and operator!=.
+ EXPECT_FALSE(uuid == uuid_zero);
+ EXPECT_NE(uuid, uuid_zero);
+
+ UUID uuid_2(kBytes);
+ EXPECT_EQ(uuid, uuid_2);
+ EXPECT_FALSE(uuid != uuid_2);
+
+ // Make sure that operator== and operator!= check the entire UUID.
+ ++uuid.data_1;
+ EXPECT_NE(uuid, uuid_2);
+ --uuid.data_1;
+ ++uuid.data_2;
+ EXPECT_NE(uuid, uuid_2);
+ --uuid.data_2;
+ ++uuid.data_3;
+ EXPECT_NE(uuid, uuid_2);
+ --uuid.data_3;
+ for (size_t index = 0; index < arraysize(uuid.data_4); ++index) {
+ ++uuid.data_4[index];
+ EXPECT_NE(uuid, uuid_2);
+ --uuid.data_4[index];
+ }
+ for (size_t index = 0; index < arraysize(uuid.data_5); ++index) {
+ ++uuid.data_5[index];
+ EXPECT_NE(uuid, uuid_2);
+ --uuid.data_5[index];
+ }
+
+ // Make sure that the UUIDs are equal again, otherwise the test above may not
+ // have been valid.
+ EXPECT_EQ(uuid, uuid_2);
+
+ const uint8_t kMoreBytes[16] = {0xff,
+ 0xee,
+ 0xdd,
+ 0xcc,
+ 0xbb,
+ 0xaa,
+ 0x99,
+ 0x88,
+ 0x77,
+ 0x66,
+ 0x55,
+ 0x44,
+ 0x33,
+ 0x22,
+ 0x11,
+ 0x00};
+ uuid.InitializeFromBytes(kMoreBytes);
+ EXPECT_EQ(0xffeeddccu, uuid.data_1);
+ EXPECT_EQ(0xbbaau, uuid.data_2);
+ EXPECT_EQ(0x9988u, uuid.data_3);
+ EXPECT_EQ(0x77u, uuid.data_4[0]);
+ EXPECT_EQ(0x66u, uuid.data_4[1]);
+ EXPECT_EQ(0x55u, uuid.data_5[0]);
+ EXPECT_EQ(0x44u, uuid.data_5[1]);
+ EXPECT_EQ(0x33u, uuid.data_5[2]);
+ EXPECT_EQ(0x22u, uuid.data_5[3]);
+ EXPECT_EQ(0x11u, uuid.data_5[4]);
+ EXPECT_EQ(0x00u, uuid.data_5[5]);
+ EXPECT_EQ("ffeeddcc-bbaa-9988-7766-554433221100", uuid.ToString());
+
+ EXPECT_NE(uuid, uuid_2);
+ EXPECT_NE(uuid, uuid_zero);
+
+ // Test that UUID is standard layout.
+ memset(&uuid, 0x45, 16);
+ EXPECT_EQ(0x45454545u, uuid.data_1);
+ EXPECT_EQ(0x4545u, uuid.data_2);
+ EXPECT_EQ(0x4545u, uuid.data_3);
+ EXPECT_EQ(0x45u, uuid.data_4[0]);
+ EXPECT_EQ(0x45u, uuid.data_4[1]);
+ EXPECT_EQ(0x45u, uuid.data_5[0]);
+ EXPECT_EQ(0x45u, uuid.data_5[1]);
+ EXPECT_EQ(0x45u, uuid.data_5[2]);
+ EXPECT_EQ(0x45u, uuid.data_5[3]);
+ EXPECT_EQ(0x45u, uuid.data_5[4]);
+ EXPECT_EQ(0x45u, uuid.data_5[5]);
+ EXPECT_EQ("45454545-4545-4545-4545-454545454545", uuid.ToString());
+
+ UUID initialized_generated(UUID::InitializeWithNewTag{});
+ EXPECT_NE(initialized_generated, uuid_zero);
+}
+
+TEST(UUID, FromString) {
+ const struct TestCase {
+ const char* uuid_string;
+ bool success;
+ } kCases[] = {
+ // Valid:
+ {"c6849cb5-fe14-4a79-8978-9ae6034c521d", true},
+ {"00000000-0000-0000-0000-000000000000", true},
+ {"ffffffff-ffff-ffff-ffff-ffffffffffff", true},
+ // Outside HEX range:
+ {"7318z10b-c453-4cef-9dc8-015655cb4bbc", false},
+ {"7318a10b-c453-4cef-9dz8-015655cb4bbc", false},
+ // Incomplete:
+ {"15655cb4-", false},
+ {"7318f10b-c453-4cef-9dc8-015655cb4bb", false},
+ {"318f10b-c453-4cef-9dc8-015655cb4bb2", false},
+ {"7318f10b-c453-4ef-9dc8-015655cb4bb2", false},
+ {"", false},
+ {"abcd", false},
+ // Trailing data:
+ {"6d247a34-53d5-40ec-a90d-d8dea9e94cc01", false}
+ };
+
+ const std::string empty_uuid = UUID().ToString();
+
+ for (size_t index = 0; index < arraysize(kCases); ++index) {
+ const TestCase& test_case = kCases[index];
+ SCOPED_TRACE(base::StringPrintf(
+ "index %" PRIuS ": %s", index, test_case.uuid_string));
+
+ UUID uuid;
+ EXPECT_EQ(test_case.success,
+ uuid.InitializeFromString(test_case.uuid_string));
+ if (test_case.success) {
+ EXPECT_EQ(test_case.uuid_string, uuid.ToString());
+ } else {
+ EXPECT_EQ(empty_uuid, uuid.ToString());
+ }
+ }
+
+ // Test for case insensitivty.
+ UUID uuid;
+ uuid.InitializeFromString("F32E5BDC-2681-4C73-A4E6-911FFD89B846");
+ EXPECT_EQ("f32e5bdc-2681-4c73-a4e6-911ffd89b846", uuid.ToString());
+
+ // Mixed case.
+ uuid.InitializeFromString("5762C15D-50b5-4171-a2e9-7429C9EC6CAB");
+ EXPECT_EQ("5762c15d-50b5-4171-a2e9-7429c9ec6cab", uuid.ToString());
+}
+
+#if defined(OS_WIN)
+
+TEST(UUID, FromSystem) {
+ ::GUID system_uuid;
+ ASSERT_EQ(RPC_S_OK, UuidCreate(&system_uuid));
+
+ UUID uuid;
+ uuid.InitializeFromSystemUUID(&system_uuid);
+
+ RPC_WSTR system_string;
+ ASSERT_EQ(RPC_S_OK, UuidToString(&system_uuid, &system_string));
+
+ struct ScopedRpcStringFreeTraits {
+ static RPC_WSTR* InvalidValue() { return nullptr; }
+ static void Free(RPC_WSTR* rpc_string) {
+ EXPECT_EQ(RPC_S_OK, RpcStringFree(rpc_string));
+ }
+ };
+ using ScopedRpcString =
+ base::ScopedGeneric<RPC_WSTR*, ScopedRpcStringFreeTraits>;
+ ScopedRpcString scoped_system_string(&system_string);
+
+ EXPECT_EQ(reinterpret_cast<wchar_t*>(system_string), uuid.ToString16());
+}
+
+#endif // OS_WIN
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/net/http_body.cc b/chromium/third_party/crashpad/crashpad/util/net/http_body.cc
new file mode 100644
index 00000000000..cdf810e37f5
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/net/http_body.cc
@@ -0,0 +1,114 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/net/http_body.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <limits>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+
+namespace crashpad {
+
+StringHTTPBodyStream::StringHTTPBodyStream(const std::string& string)
+ : HTTPBodyStream(), string_(string), bytes_read_() {
+}
+
+StringHTTPBodyStream::~StringHTTPBodyStream() {
+}
+
+ssize_t StringHTTPBodyStream::GetBytesBuffer(uint8_t* buffer, size_t max_len) {
+ size_t num_bytes_remaining = string_.length() - bytes_read_;
+ if (num_bytes_remaining == 0) {
+ return num_bytes_remaining;
+ }
+
+ size_t num_bytes_returned =
+ std::min(std::min(num_bytes_remaining, max_len),
+ implicit_cast<size_t>(std::numeric_limits<ssize_t>::max()));
+ memcpy(buffer, &string_[bytes_read_], num_bytes_returned);
+ bytes_read_ += num_bytes_returned;
+ return num_bytes_returned;
+}
+
+FileHTTPBodyStream::FileHTTPBodyStream(const base::FilePath& path)
+ : HTTPBodyStream(), path_(path), file_(), file_state_(kUnopenedFile) {
+}
+
+FileHTTPBodyStream::~FileHTTPBodyStream() {
+}
+
+ssize_t FileHTTPBodyStream::GetBytesBuffer(uint8_t* buffer, size_t max_len) {
+ switch (file_state_) {
+ case kUnopenedFile:
+ file_.reset(LoggingOpenFileForRead(path_));
+ if (!file_.is_valid()) {
+ file_state_ = kFileOpenError;
+ return -1;
+ }
+ file_state_ = kReading;
+ break;
+ case kFileOpenError:
+ return -1;
+ case kClosedAtEOF:
+ return 0;
+ case kReading:
+ break;
+ }
+
+ ssize_t rv = ReadFile(file_.get(), buffer, max_len);
+ if (rv == 0) {
+ file_.reset();
+ file_state_ = kClosedAtEOF;
+ } else if (rv < 0) {
+ PLOG(ERROR) << "read";
+ }
+ return rv;
+}
+
+CompositeHTTPBodyStream::CompositeHTTPBodyStream(
+ const CompositeHTTPBodyStream::PartsList& parts)
+ : HTTPBodyStream(), parts_(parts), current_part_(parts_.begin()) {
+}
+
+CompositeHTTPBodyStream::~CompositeHTTPBodyStream() {
+ STLDeleteContainerPointers(parts_.begin(), parts_.end());
+}
+
+ssize_t CompositeHTTPBodyStream::GetBytesBuffer(uint8_t* buffer,
+ size_t buffer_len) {
+ ssize_t max_len = std::min(
+ buffer_len, implicit_cast<size_t>(std::numeric_limits<ssize_t>::max()));
+ ssize_t bytes_copied = 0;
+ while (bytes_copied < max_len && current_part_ != parts_.end()) {
+ ssize_t this_read = (*current_part_)->GetBytesBuffer(
+ buffer + bytes_copied, max_len - bytes_copied);
+
+ if (this_read == 0) {
+ // If the current part has returned 0 indicating EOF, advance the current
+ // part and try again.
+ ++current_part_;
+ } else if (this_read < 0) {
+ return this_read;
+ }
+ bytes_copied += this_read;
+ }
+
+ return bytes_copied;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/net/http_body.h b/chromium/third_party/crashpad/crashpad/util/net/http_body.h
new file mode 100644
index 00000000000..3cc2f19d01d
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/net/http_body.h
@@ -0,0 +1,129 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_NET_HTTP_BODY_H_
+#define CRASHPAD_UTIL_NET_HTTP_BODY_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "util/file/file_io.h"
+
+namespace crashpad {
+
+//! \brief An interface to a stream that can be used for an HTTP request body.
+class HTTPBodyStream {
+ public:
+ virtual ~HTTPBodyStream() {}
+
+ //! \brief Copies up to \a max_len bytes into the user-supplied buffer.
+ //!
+ //! \param[out] buffer A user-supplied buffer into which this method will copy
+ //! bytes from the stream.
+ //! \param[in] max_len The length (or size) of \a buffer. At most this many
+ //! bytes will be copied.
+ //!
+ //! \return On success, a positive number indicating the number of bytes
+ //! actually copied to \a buffer. On failure, a negative number. When
+ //! the stream has no more data, returns `0`.
+ virtual ssize_t GetBytesBuffer(uint8_t* buffer, size_t max_len) = 0;
+
+ protected:
+ HTTPBodyStream() {}
+};
+
+//! \brief An implementation of HTTPBodyStream that turns a fixed string into
+//! a stream.
+class StringHTTPBodyStream : public HTTPBodyStream {
+ public:
+ //! \brief Creates a stream with the specified string.
+ //!
+ //! \param[in] string The string to turn into a stream.
+ explicit StringHTTPBodyStream(const std::string& string);
+
+ ~StringHTTPBodyStream() override;
+
+ // HTTPBodyStream:
+ ssize_t GetBytesBuffer(uint8_t* buffer, size_t max_len) override;
+
+ private:
+ std::string string_;
+ size_t bytes_read_;
+
+ DISALLOW_COPY_AND_ASSIGN(StringHTTPBodyStream);
+};
+
+//! \brief An implementation of HTTPBodyStream that reads from the specified
+//! file and provides its contents for an HTTP body.
+class FileHTTPBodyStream : public HTTPBodyStream {
+ public:
+ //! \brief Creates a stream for reading the file at the specified \a path.
+ //!
+ //! \param[in] path The file from which this HTTPBodyStream will read.
+ explicit FileHTTPBodyStream(const base::FilePath& path);
+
+ ~FileHTTPBodyStream() override;
+
+ // HTTPBodyStream:
+ ssize_t GetBytesBuffer(uint8_t* buffer, size_t max_len) override;
+
+ private:
+ enum FileState {
+ kUnopenedFile,
+ kFileOpenError,
+ kClosedAtEOF,
+ kReading,
+ };
+
+ base::FilePath path_;
+ ScopedFileHandle file_;
+ FileState file_state_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileHTTPBodyStream);
+};
+
+//! \brief An implementation of HTTPBodyStream that combines an array of
+//! several other HTTPBodyStream objects into a single, unified stream.
+class CompositeHTTPBodyStream : public HTTPBodyStream {
+ public:
+ using PartsList = std::vector<HTTPBodyStream*>;
+
+ //! \brief Creates a stream from an array of other stream parts.
+ //!
+ //! \param[in] parts A vector of HTTPBodyStream objects, of which this object
+ //! takes ownership, that will be represented as a single unified stream.
+ //! Callers should not mutate the stream objects after passing them to
+ //! an instance of this class.
+ explicit CompositeHTTPBodyStream(const PartsList& parts);
+
+ ~CompositeHTTPBodyStream() override;
+
+ // HTTPBodyStream:
+ ssize_t GetBytesBuffer(uint8_t* buffer, size_t max_len) override;
+
+ private:
+ PartsList parts_;
+ PartsList::iterator current_part_;
+
+ DISALLOW_COPY_AND_ASSIGN(CompositeHTTPBodyStream);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_NET_HTTP_BODY_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/net/http_body_test.cc b/chromium/third_party/crashpad/crashpad/util/net/http_body_test.cc
new file mode 100644
index 00000000000..6bfcff36b08
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/net/http_body_test.cc
@@ -0,0 +1,217 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/net/http_body.h"
+
+#include "gtest/gtest.h"
+#include "test/paths.h"
+#include "util/net/http_body_test_util.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+void ExpectBufferSet(const uint8_t* actual,
+ uint8_t expected_byte,
+ size_t num_expected_bytes) {
+ for (size_t i = 0; i < num_expected_bytes; ++i) {
+ EXPECT_EQ(expected_byte, actual[i]) << i;
+ }
+}
+
+TEST(StringHTTPBodyStream, EmptyString) {
+ uint8_t buf[32];
+ memset(buf, '!', sizeof(buf));
+
+ std::string empty_string;
+ StringHTTPBodyStream stream(empty_string);
+ EXPECT_EQ(0, stream.GetBytesBuffer(buf, sizeof(buf)));
+ ExpectBufferSet(buf, '!', sizeof(buf));
+}
+
+TEST(StringHTTPBodyStream, SmallString) {
+ uint8_t buf[32];
+ memset(buf, '!', sizeof(buf));
+
+ std::string string("Hello, world");
+ StringHTTPBodyStream stream(string);
+ EXPECT_EQ(implicit_cast<ssize_t>(string.length()),
+ stream.GetBytesBuffer(buf, sizeof(buf)));
+
+ std::string actual(reinterpret_cast<const char*>(buf), string.length());
+ EXPECT_EQ(string, actual);
+ ExpectBufferSet(buf + string.length(), '!', sizeof(buf) - string.length());
+
+ EXPECT_EQ(0, stream.GetBytesBuffer(buf, sizeof(buf)));
+}
+
+TEST(StringHTTPBodyStream, MultipleReads) {
+ uint8_t buf[2];
+ memset(buf, '!', sizeof(buf));
+
+ {
+ std::string string("test");
+ SCOPED_TRACE("aligned buffer boundary");
+
+ StringHTTPBodyStream stream(string);
+ EXPECT_EQ(2, stream.GetBytesBuffer(buf, sizeof(buf)));
+ EXPECT_EQ('t', buf[0]);
+ EXPECT_EQ('e', buf[1]);
+ EXPECT_EQ(2, stream.GetBytesBuffer(buf, sizeof(buf)));
+ EXPECT_EQ('s', buf[0]);
+ EXPECT_EQ('t', buf[1]);
+ EXPECT_EQ(0, stream.GetBytesBuffer(buf, sizeof(buf)));
+ EXPECT_EQ('s', buf[0]);
+ EXPECT_EQ('t', buf[1]);
+ }
+
+ {
+ std::string string("abc");
+ SCOPED_TRACE("unaligned buffer boundary");
+
+ StringHTTPBodyStream stream(string);
+ EXPECT_EQ(2, stream.GetBytesBuffer(buf, sizeof(buf)));
+ EXPECT_EQ('a', buf[0]);
+ EXPECT_EQ('b', buf[1]);
+ EXPECT_EQ(1, stream.GetBytesBuffer(buf, sizeof(buf)));
+ EXPECT_EQ('c', buf[0]);
+ EXPECT_EQ('b', buf[1]); // Unmodified from last read.
+ EXPECT_EQ(0, stream.GetBytesBuffer(buf, sizeof(buf)));
+ EXPECT_EQ('c', buf[0]);
+ EXPECT_EQ('b', buf[1]);
+ }
+}
+
+TEST(FileHTTPBodyStream, ReadASCIIFile) {
+ base::FilePath path = Paths::TestDataRoot().Append(
+ FILE_PATH_LITERAL("util/net/testdata/ascii_http_body.txt"));
+ FileHTTPBodyStream stream(path);
+ std::string contents = ReadStreamToString(&stream, 32);
+ EXPECT_EQ("This is a test.\n", contents);
+
+ // Make sure that the file is not read again after it has been read to
+ // completion.
+ uint8_t buf[8];
+ memset(buf, '!', sizeof(buf));
+ EXPECT_EQ(0, stream.GetBytesBuffer(buf, sizeof(buf)));
+ ExpectBufferSet(buf, '!', sizeof(buf));
+ EXPECT_EQ(0, stream.GetBytesBuffer(buf, sizeof(buf)));
+ ExpectBufferSet(buf, '!', sizeof(buf));
+}
+
+TEST(FileHTTPBodyStream, ReadBinaryFile) {
+ // HEX contents of file: |FEEDFACE A11A15|.
+ base::FilePath path = Paths::TestDataRoot().Append(
+ FILE_PATH_LITERAL("util/net/testdata/binary_http_body.dat"));
+ // This buffer size was chosen so that reading the file takes multiple reads.
+ uint8_t buf[4];
+
+ FileHTTPBodyStream stream(path);
+
+ memset(buf, '!', sizeof(buf));
+ EXPECT_EQ(4, stream.GetBytesBuffer(buf, sizeof(buf)));
+ EXPECT_EQ(0xfe, buf[0]);
+ EXPECT_EQ(0xed, buf[1]);
+ EXPECT_EQ(0xfa, buf[2]);
+ EXPECT_EQ(0xce, buf[3]);
+
+ memset(buf, '!', sizeof(buf));
+ EXPECT_EQ(3, stream.GetBytesBuffer(buf, sizeof(buf)));
+ EXPECT_EQ(0xa1, buf[0]);
+ EXPECT_EQ(0x1a, buf[1]);
+ EXPECT_EQ(0x15, buf[2]);
+ EXPECT_EQ('!', buf[3]);
+
+ memset(buf, '!', sizeof(buf));
+ EXPECT_EQ(0, stream.GetBytesBuffer(buf, sizeof(buf)));
+ ExpectBufferSet(buf, '!', sizeof(buf));
+ EXPECT_EQ(0, stream.GetBytesBuffer(buf, sizeof(buf)));
+ ExpectBufferSet(buf, '!', sizeof(buf));
+}
+
+TEST(FileHTTPBodyStream, NonExistentFile) {
+ base::FilePath path = base::FilePath(
+ FILE_PATH_LITERAL("/var/empty/crashpad/util/net/http_body/null"));
+ FileHTTPBodyStream stream(path);
+
+ uint8_t buf = 0xff;
+ EXPECT_LT(stream.GetBytesBuffer(&buf, 1), 0);
+ EXPECT_EQ(0xff, buf);
+ EXPECT_LT(stream.GetBytesBuffer(&buf, 1), 0);
+ EXPECT_EQ(0xff, buf);
+}
+
+TEST(CompositeHTTPBodyStream, TwoEmptyStrings) {
+ std::vector<HTTPBodyStream*> parts;
+ parts.push_back(new StringHTTPBodyStream(std::string()));
+ parts.push_back(new StringHTTPBodyStream(std::string()));
+
+ CompositeHTTPBodyStream stream(parts);
+
+ uint8_t buf[5];
+ memset(buf, '!', sizeof(buf));
+ EXPECT_EQ(0, stream.GetBytesBuffer(buf, sizeof(buf)));
+ ExpectBufferSet(buf, '!', sizeof(buf));
+}
+
+class CompositeHTTPBodyStreamBufferSize
+ : public testing::TestWithParam<size_t> {
+};
+
+TEST_P(CompositeHTTPBodyStreamBufferSize, ThreeStringParts) {
+ std::string string1("crashpad");
+ std::string string2("test");
+ std::string string3("foobar");
+ const size_t all_strings_length =
+ string1.length() + string2.length() + string3.length();
+ std::string buf(all_strings_length + 3, '!');
+
+ std::vector<HTTPBodyStream*> parts;
+ parts.push_back(new StringHTTPBodyStream(string1));
+ parts.push_back(new StringHTTPBodyStream(string2));
+ parts.push_back(new StringHTTPBodyStream(string3));
+
+ CompositeHTTPBodyStream stream(parts);
+
+ std::string actual_string = ReadStreamToString(&stream, GetParam());
+ EXPECT_EQ(string1 + string2 + string3, actual_string);
+
+ ExpectBufferSet(reinterpret_cast<uint8_t*>(&buf[all_strings_length]), '!', 3);
+}
+
+TEST_P(CompositeHTTPBodyStreamBufferSize, StringsAndFile) {
+ std::string string1("Hello! ");
+ std::string string2(" Goodbye :)");
+
+ std::vector<HTTPBodyStream*> parts;
+ parts.push_back(new StringHTTPBodyStream(string1));
+ base::FilePath path = Paths::TestDataRoot().Append(
+ FILE_PATH_LITERAL("util/net/testdata/ascii_http_body.txt"));
+ parts.push_back(new FileHTTPBodyStream(path));
+ parts.push_back(new StringHTTPBodyStream(string2));
+
+ CompositeHTTPBodyStream stream(parts);
+
+ std::string expected_string = string1 + "This is a test.\n" + string2;
+ std::string actual_string = ReadStreamToString(&stream, GetParam());
+ EXPECT_EQ(expected_string, actual_string);
+}
+
+INSTANTIATE_TEST_CASE_P(VariableBufferSize,
+ CompositeHTTPBodyStreamBufferSize,
+ testing::Values(1, 2, 9, 16, 31, 128, 1024));
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/net/http_body_test_util.cc b/chromium/third_party/crashpad/crashpad/util/net/http_body_test_util.cc
new file mode 100644
index 00000000000..3d732311078
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/net/http_body_test_util.cc
@@ -0,0 +1,48 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/net/http_body_test_util.h"
+
+#include <stdint.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "gtest/gtest.h"
+#include "util/net/http_body.h"
+
+namespace crashpad {
+namespace test {
+
+std::string ReadStreamToString(HTTPBodyStream* stream) {
+ return ReadStreamToString(stream, 32);
+}
+
+std::string ReadStreamToString(HTTPBodyStream* stream, size_t buffer_size) {
+ scoped_ptr<uint8_t[]> buf(new uint8_t[buffer_size]);
+ std::string result;
+
+ ssize_t bytes_read;
+ while ((bytes_read = stream->GetBytesBuffer(buf.get(), buffer_size)) != 0) {
+ if (bytes_read < 0) {
+ ADD_FAILURE() << "Failed to read from stream: " << bytes_read;
+ return std::string();
+ }
+
+ result.append(reinterpret_cast<char*>(buf.get()), bytes_read);
+ }
+
+ return result;
+}
+
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/net/http_body_test_util.h b/chromium/third_party/crashpad/crashpad/util/net/http_body_test_util.h
new file mode 100644
index 00000000000..288fccaeb68
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/net/http_body_test_util.h
@@ -0,0 +1,49 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_NET_HTTP_BODY_TEST_UTIL_H_
+#define CRASHPAD_UTIL_NET_HTTP_BODY_TEST_UTIL_H_
+
+#include <sys/types.h>
+
+#include <string>
+
+namespace crashpad {
+
+class HTTPBodyStream;
+
+namespace test {
+
+//! \brief Reads a HTTPBodyStream to a string. If an error occurs, adds a
+//! test failure and returns an empty string.
+//!
+//! \param[in] stream The stream from which to read.
+//!
+//! \return The contents of the stream, or an empty string on failure.
+std::string ReadStreamToString(HTTPBodyStream* stream);
+
+//! \brief Reads a HTTPBodyStream to a string. If an error occurs, adds a
+//! test failure and returns an empty string.
+//!
+//! \param[in] stream The stream from which to read.
+//! \param[in] buffer_size The size of the buffer to use when reading from the
+//! stream.
+//!
+//! \return The contents of the stream, or an empty string on failure.
+std::string ReadStreamToString(HTTPBodyStream* stream, size_t buffer_size);
+
+} // namespace test
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_NET_HTTP_BODY_TEST_UTIL_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/net/http_headers.cc b/chromium/third_party/crashpad/crashpad/util/net/http_headers.cc
new file mode 100644
index 00000000000..09d61b3e44c
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/net/http_headers.cc
@@ -0,0 +1,23 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/net/http_headers.h"
+
+namespace crashpad {
+
+const char kContentType[] = "Content-Type";
+
+const char kContentLength[] = "Content-Length";
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/net/http_headers.h b/chromium/third_party/crashpad/crashpad/util/net/http_headers.h
new file mode 100644
index 00000000000..3633cb2d690
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/net/http_headers.h
@@ -0,0 +1,34 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_NET_HTTP_HEADERS_H_
+#define CRASHPAD_UTIL_NET_HTTP_HEADERS_H_
+
+#include <map>
+#include <string>
+
+namespace crashpad {
+
+//! \brief A map of HTTP header fields to their values.
+using HTTPHeaders = std::map<std::string, std::string>;
+
+//! \brief The header name `"Content-Type"`.
+extern const char kContentType[];
+
+//! \brief The header name `"Content-Length"`.
+extern const char kContentLength[];
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_NET_HTTP_HEADERS_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/net/http_multipart_builder.cc b/chromium/third_party/crashpad/crashpad/util/net/http_multipart_builder.cc
new file mode 100644
index 00000000000..83186af67ed
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/net/http_multipart_builder.cc
@@ -0,0 +1,199 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/net/http_multipart_builder.h"
+
+#include <utility>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "base/strings/stringprintf.h"
+#include "util/net/http_body.h"
+
+namespace crashpad {
+
+namespace {
+
+const char kCRLF[] = "\r\n";
+
+const char kBoundaryCRLF[] = "\r\n\r\n";
+
+// Generates a random string suitable for use as a multipart boundary.
+std::string GenerateBoundaryString() {
+ // RFC 2046 §5.1.1 says that the boundary string may be 1 to 70 characters
+ // long, choosing from the set of alphanumeric characters along with
+ // characters from the set “'()+_,-./:=? ”, and not ending in a space.
+ // However, some servers have been observed as dealing poorly with certain
+ // nonalphanumeric characters. See
+ // blink/Source/platform/network/FormDataBuilder.cpp
+ // blink::FormDataBuilder::generateUniqueBoundaryString().
+ //
+ // This implementation produces a 56-character string with over 190 bits of
+ // randomness (62^32 > 2^190).
+ std::string boundary_string = "---MultipartBoundary-";
+ for (int index = 0; index < 32; ++index) {
+ const char kCharacters[] =
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+ int random_value =
+ base::RandInt(0, static_cast<int>(strlen(kCharacters)) - 1);
+ boundary_string += kCharacters[random_value];
+ }
+ boundary_string += "---";
+ return boundary_string;
+}
+
+// Escapes the specified name to be suitable for the name field of a
+// form-data part.
+std::string EncodeMIMEField(const std::string& name) {
+ // RFC 2388 §3 says to encode non-ASCII field names according to RFC 2047, but
+ // no browsers implement that behavior. Instead, they send field names in the
+ // page hosting the form’s encoding. However, some form of escaping is needed.
+ // This URL-escapes the quote character and newline characters, per Blink. See
+ // blink/Source/platform/network/FormDataBuilder.cpp
+ // blink::appendQuotedString().
+ //
+ // TODO(mark): This encoding is not necessarily correct, and the same code in
+ // Blink is marked with a FIXME. Blink does not escape the '%' character,
+ // that’s a local addition, but it seems appropriate to be able to decode the
+ // string properly.
+ std::string encoded;
+ for (char character : name) {
+ switch (character) {
+ case '\r':
+ case '\n':
+ case '"':
+ case '%':
+ encoded += base::StringPrintf("%%%02x", character);
+ break;
+ default:
+ encoded += character;
+ break;
+ }
+ }
+
+ return encoded;
+}
+
+// Returns a string, formatted with a multipart boundary and a field name,
+// after which the contents of the part at |name| can be appended.
+std::string GetFormDataBoundary(const std::string& boundary,
+ const std::string& name) {
+ return base::StringPrintf(
+ "--%s%sContent-Disposition: form-data; name=\"%s\"",
+ boundary.c_str(),
+ kCRLF,
+ EncodeMIMEField(name).c_str());
+}
+
+void AssertSafeMIMEType(const std::string& string) {
+ for (size_t i = 0; i < string.length(); ++i) {
+ char c = string[i];
+ CHECK((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') ||
+ c == '/' ||
+ c == '.' ||
+ c == '_' ||
+ c == '+' ||
+ c == '-');
+ }
+}
+
+} // namespace
+
+HTTPMultipartBuilder::HTTPMultipartBuilder()
+ : boundary_(GenerateBoundaryString()), form_data_(), file_attachments_() {
+}
+
+HTTPMultipartBuilder::~HTTPMultipartBuilder() {
+}
+
+void HTTPMultipartBuilder::SetFormData(const std::string& key,
+ const std::string& value) {
+ EraseKey(key);
+ form_data_[key] = value;
+}
+
+void HTTPMultipartBuilder::SetFileAttachment(
+ const std::string& key,
+ const std::string& upload_file_name,
+ const base::FilePath& path,
+ const std::string& content_type) {
+ EraseKey(upload_file_name);
+
+ FileAttachment attachment;
+ attachment.filename = EncodeMIMEField(upload_file_name);
+ attachment.path = path;
+
+ if (content_type.empty()) {
+ attachment.content_type = "application/octet-stream";
+ } else {
+ AssertSafeMIMEType(content_type);
+ attachment.content_type = content_type;
+ }
+
+ file_attachments_[key] = attachment;
+}
+
+scoped_ptr<HTTPBodyStream> HTTPMultipartBuilder::GetBodyStream() {
+ // The objects inserted into this vector will be owned by the returned
+ // CompositeHTTPBodyStream. Take care to not early-return without deleting
+ // this memory.
+ std::vector<HTTPBodyStream*> streams;
+
+ for (const auto& pair : form_data_) {
+ std::string field = GetFormDataBoundary(boundary_, pair.first);
+ field += kBoundaryCRLF;
+ field += pair.second;
+ field += kCRLF;
+ streams.push_back(new StringHTTPBodyStream(field));
+ }
+
+ for (const auto& pair : file_attachments_) {
+ const FileAttachment& attachment = pair.second;
+ std::string header = GetFormDataBoundary(boundary_, pair.first);
+ header += base::StringPrintf("; filename=\"%s\"%s",
+ attachment.filename.c_str(), kCRLF);
+ header += base::StringPrintf("Content-Type: %s%s",
+ attachment.content_type.c_str(), kBoundaryCRLF);
+
+ streams.push_back(new StringHTTPBodyStream(header));
+ streams.push_back(new FileHTTPBodyStream(attachment.path));
+ streams.push_back(new StringHTTPBodyStream(kCRLF));
+ }
+
+ streams.push_back(
+ new StringHTTPBodyStream("--" + boundary_ + "--" + kCRLF));
+
+ return scoped_ptr<HTTPBodyStream>(new CompositeHTTPBodyStream(streams));
+}
+
+HTTPHeaders::value_type HTTPMultipartBuilder::GetContentType() const {
+ std::string content_type =
+ base::StringPrintf("multipart/form-data; boundary=%s", boundary_.c_str());
+ return std::make_pair(kContentType, content_type);
+}
+
+void HTTPMultipartBuilder::EraseKey(const std::string& key) {
+ auto data_it = form_data_.find(key);
+ if (data_it != form_data_.end())
+ form_data_.erase(data_it);
+
+ auto file_it = file_attachments_.find(key);
+ if (file_it != file_attachments_.end())
+ file_attachments_.erase(file_it);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/net/http_multipart_builder.h b/chromium/third_party/crashpad/crashpad/util/net/http_multipart_builder.h
new file mode 100644
index 00000000000..c9e03ce739e
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/net/http_multipart_builder.h
@@ -0,0 +1,90 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_NET_HTTP_MULTIPART_BUILDER_H_
+#define CRASHPAD_UTIL_NET_HTTP_MULTIPART_BUILDER_H_
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "util/net/http_headers.h"
+
+namespace crashpad {
+
+class HTTPBodyStream;
+
+//! \brief This class is used to build a MIME multipart message, conforming to
+//! RFC 2046, for use as a HTTP request body.
+class HTTPMultipartBuilder {
+ public:
+ HTTPMultipartBuilder();
+ ~HTTPMultipartBuilder();
+
+ //! \brief Sets a `Content-Disposition: form-data` key-value pair.
+ //!
+ //! \param[in] key The key of the form data, specified as the `name` in the
+ //! multipart message. Any data previously set on this class with this
+ //! key will be overwritten.
+ //! \param[in] value The value to set at the \a key.
+ void SetFormData(const std::string& key, const std::string& value);
+
+ //! \brief Specifies the file at \a path to have its contents uploaded as
+ //! multipart data, available at `name` of \a upload_file_name.
+ //!
+ //! \param[in] key The key of the form data, specified as the `name` in the
+ //! multipart message. Any data previously set on this class with this
+ //! key will be overwritten.
+ //! \param[in] upload_file_name The `filename` to specify for this multipart
+ //! data attachment.
+ //! \param[in] path The path of the file whose contents will be uploaded.
+ //! \param[in] content_type The `Content-Type` to specify for the attachment.
+ //! If this is empty, `"application/octet-stream"` will be used.
+ void SetFileAttachment(const std::string& key,
+ const std::string& upload_file_name,
+ const base::FilePath& path,
+ const std::string& content_type);
+
+ //! \brief Generates the HTTPBodyStream for the data currently supplied to
+ //! the builder.
+ //!
+ //! \return A caller-owned HTTPBodyStream object.
+ scoped_ptr<HTTPBodyStream> GetBodyStream();
+
+ //! \brief Gets the header pair for `"Content-Type"`.
+ HTTPHeaders::value_type GetContentType() const;
+
+ private:
+ struct FileAttachment {
+ std::string filename;
+ std::string content_type;
+ base::FilePath path;
+ };
+
+ // Removes elements from both data maps at the specified |key|, to ensure
+ // uniqueness across the entire HTTP body.
+ void EraseKey(const std::string& key);
+
+ std::string boundary_;
+ std::map<std::string, std::string> form_data_;
+ std::map<std::string, FileAttachment> file_attachments_;
+
+ DISALLOW_COPY_AND_ASSIGN(HTTPMultipartBuilder);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_NET_HTTP_MULTIPART_BUILDER_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/net/http_multipart_builder_test.cc b/chromium/third_party/crashpad/crashpad/util/net/http_multipart_builder_test.cc
new file mode 100644
index 00000000000..d024c520fdf
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/net/http_multipart_builder_test.cc
@@ -0,0 +1,293 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/net/http_multipart_builder.h"
+
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "test/gtest_death_check.h"
+#include "test/paths.h"
+#include "util/net/http_body.h"
+#include "util/net/http_body_test_util.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+std::vector<std::string> SplitCRLF(const std::string& string) {
+ std::vector<std::string> lines;
+ size_t last_line = 0;
+ for (size_t i = 0; i < string.length(); ++i) {
+ if (string[i] == '\r' && i+1 < string.length() && string[i+1] == '\n') {
+ lines.push_back(string.substr(last_line, i - last_line));
+ last_line = i + 2;
+ ++i;
+ }
+ }
+ // Append any remainder.
+ if (last_line < string.length()) {
+ lines.push_back(string.substr(last_line));
+ }
+ return lines;
+}
+
+// In the tests below, the form data pairs don’t appear in the order they were
+// added. The current implementation uses a std::map which sorts keys, so the
+// entires appear in alphabetical order. However, this is an implementation
+// detail, and it’s OK if the writer stops sorting in this order. Testing for
+// a specific order is just the easiest way to write this test while the writer
+// will output things in a known order.
+
+TEST(HTTPMultipartBuilder, ThreeStringFields) {
+ HTTPMultipartBuilder builder;
+
+ const char kKey1[] = "key1";
+ const char kValue1[] = "test";
+ builder.SetFormData(kKey1, kValue1);
+
+ const char kKey2[] = "key2";
+ const char kValue2[] = "This is another test.";
+ builder.SetFormData(kKey2, kValue2);
+
+ const char kKey3[] = "key-three";
+ const char kValue3[] = "More tests";
+ builder.SetFormData(kKey3, kValue3);
+
+ scoped_ptr<HTTPBodyStream> body(builder.GetBodyStream());
+ ASSERT_TRUE(body.get());
+ std::string contents = ReadStreamToString(body.get());
+ auto lines = SplitCRLF(contents);
+ auto lines_it = lines.begin();
+
+ // The first line is the boundary. All subsequent boundaries must match this.
+ const std::string& boundary = *lines_it++;
+ EXPECT_GE(boundary.length(), 1u);
+ EXPECT_LE(boundary.length(), 70u);
+
+ EXPECT_EQ("Content-Disposition: form-data; name=\"key-three\"", *lines_it++);
+ EXPECT_EQ("", *lines_it++);
+ EXPECT_EQ(kValue3, *lines_it++);
+
+ EXPECT_EQ(boundary, *lines_it++);
+ EXPECT_EQ("Content-Disposition: form-data; name=\"key1\"", *lines_it++);
+ EXPECT_EQ("", *lines_it++);
+ EXPECT_EQ(kValue1, *lines_it++);
+
+ EXPECT_EQ(boundary, *lines_it++);
+ EXPECT_EQ("Content-Disposition: form-data; name=\"key2\"", *lines_it++);
+ EXPECT_EQ("", *lines_it++);
+ EXPECT_EQ(kValue2, *lines_it++);
+
+ EXPECT_EQ(boundary + "--", *lines_it++);
+
+ EXPECT_EQ(lines.end(), lines_it);
+}
+
+TEST(HTTPMultipartBuilder, ThreeFileAttachments) {
+ HTTPMultipartBuilder builder;
+ base::FilePath ascii_http_body_path = Paths::TestDataRoot().Append(
+ FILE_PATH_LITERAL("util/net/testdata/ascii_http_body.txt"));
+ builder.SetFileAttachment("first",
+ "minidump.dmp",
+ ascii_http_body_path,
+ "");
+ builder.SetFileAttachment("second",
+ "minidump.dmp",
+ ascii_http_body_path,
+ "text/plain");
+ builder.SetFileAttachment("\"third 50% silly\"",
+ "test%foo.txt",
+ ascii_http_body_path,
+ "text/plain");
+
+ const char kFileContents[] = "This is a test.\n";
+
+ scoped_ptr<HTTPBodyStream> body(builder.GetBodyStream());
+ ASSERT_TRUE(body.get());
+ std::string contents = ReadStreamToString(body.get());
+ auto lines = SplitCRLF(contents);
+ ASSERT_EQ(16u, lines.size());
+ auto lines_it = lines.begin();
+
+ const std::string& boundary = *lines_it++;
+ EXPECT_GE(boundary.length(), 1u);
+ EXPECT_LE(boundary.length(), 70u);
+
+ EXPECT_EQ("Content-Disposition: form-data; "
+ "name=\"%22third 50%25 silly%22\"; filename=\"test%25foo.txt\"",
+ *lines_it++);
+ EXPECT_EQ("Content-Type: text/plain", *lines_it++);
+ EXPECT_EQ("", *lines_it++);
+ EXPECT_EQ(kFileContents, *lines_it++);
+
+ EXPECT_EQ(boundary, *lines_it++);
+ EXPECT_EQ("Content-Disposition: form-data; "
+ "name=\"first\"; filename=\"minidump.dmp\"",
+ *lines_it++);
+ EXPECT_EQ("Content-Type: application/octet-stream", *lines_it++);
+ EXPECT_EQ("", *lines_it++);
+ EXPECT_EQ(kFileContents, *lines_it++);
+
+ EXPECT_EQ(boundary, *lines_it++);
+ EXPECT_EQ("Content-Disposition: form-data; "
+ "name=\"second\"; filename=\"minidump.dmp\"",
+ *lines_it++);
+ EXPECT_EQ("Content-Type: text/plain", *lines_it++);
+ EXPECT_EQ("", *lines_it++);
+ EXPECT_EQ(kFileContents, *lines_it++);
+
+ EXPECT_EQ(boundary + "--", *lines_it++);
+
+ EXPECT_EQ(lines.end(), lines_it);
+}
+
+TEST(HTTPMultipartBuilder, OverwriteFormDataWithEscapedKey) {
+ HTTPMultipartBuilder builder;
+ const char kKey[] = "a 100% \"silly\"\r\ntest";
+ builder.SetFormData(kKey, "some dummy value");
+ builder.SetFormData(kKey, "overwrite");
+ scoped_ptr<HTTPBodyStream> body(builder.GetBodyStream());
+ ASSERT_TRUE(body.get());
+ std::string contents = ReadStreamToString(body.get());
+ auto lines = SplitCRLF(contents);
+ auto lines_it = lines.begin();
+
+ const std::string& boundary = *lines_it++;
+ EXPECT_GE(boundary.length(), 1u);
+ EXPECT_LE(boundary.length(), 70u);
+
+ EXPECT_EQ(
+ "Content-Disposition: form-data; name=\"a 100%25 %22silly%22%0d%0atest\"",
+ *lines_it++);
+ EXPECT_EQ("", *lines_it++);
+ EXPECT_EQ("overwrite", *lines_it++);
+ EXPECT_EQ(boundary + "--", *lines_it++);
+ EXPECT_EQ(lines.end(), lines_it);
+}
+
+TEST(HTTPMultipartBuilder, OverwriteFileAttachment) {
+ HTTPMultipartBuilder builder;
+ const char kValue[] = "1 2 3 test";
+ builder.SetFormData("a key", kValue);
+ base::FilePath testdata_path =
+ Paths::TestDataRoot().Append(FILE_PATH_LITERAL("util/net/testdata"));
+ builder.SetFileAttachment("minidump",
+ "minidump.dmp",
+ testdata_path.Append(FILE_PATH_LITERAL(
+ "binary_http_body.dat")),
+ "");
+ builder.SetFileAttachment("minidump2",
+ "minidump.dmp",
+ testdata_path.Append(FILE_PATH_LITERAL(
+ "binary_http_body.dat")),
+ "");
+ builder.SetFileAttachment("minidump",
+ "minidump.dmp",
+ testdata_path.Append(FILE_PATH_LITERAL(
+ "ascii_http_body.txt")),
+ "text/plain");
+ scoped_ptr<HTTPBodyStream> body(builder.GetBodyStream());
+ ASSERT_TRUE(body.get());
+ std::string contents = ReadStreamToString(body.get());
+ auto lines = SplitCRLF(contents);
+ ASSERT_EQ(15u, lines.size());
+ auto lines_it = lines.begin();
+
+ const std::string& boundary = *lines_it++;
+ EXPECT_GE(boundary.length(), 1u);
+ EXPECT_LE(boundary.length(), 70u);
+
+ EXPECT_EQ("Content-Disposition: form-data; name=\"a key\"", *lines_it++);
+ EXPECT_EQ("", *lines_it++);
+ EXPECT_EQ(kValue, *lines_it++);
+
+ EXPECT_EQ(boundary, *lines_it++);
+ EXPECT_EQ("Content-Disposition: form-data; "
+ "name=\"minidump\"; filename=\"minidump.dmp\"",
+ *lines_it++);
+ EXPECT_EQ("Content-Type: text/plain", *lines_it++);
+ EXPECT_EQ("", *lines_it++);
+ EXPECT_EQ("This is a test.\n", *lines_it++);
+
+ EXPECT_EQ(boundary, *lines_it++);
+ EXPECT_EQ("Content-Disposition: form-data; "
+ "name=\"minidump2\"; filename=\"minidump.dmp\"",
+ *lines_it++);
+ EXPECT_EQ("Content-Type: application/octet-stream", *lines_it++);
+ EXPECT_EQ("", *lines_it++);
+ EXPECT_EQ("\xFE\xED\xFA\xCE\xA1\x1A\x15", *lines_it++);
+
+ EXPECT_EQ(boundary + "--", *lines_it++);
+
+ EXPECT_EQ(lines.end(), lines_it);
+}
+
+TEST(HTTPMultipartBuilder, SharedFormDataAndAttachmentKeyNamespace) {
+ HTTPMultipartBuilder builder;
+ const char kValue1[] = "11111";
+ builder.SetFormData("one", kValue1);
+ base::FilePath ascii_http_body_path = Paths::TestDataRoot().Append(
+ FILE_PATH_LITERAL("util/net/testdata/ascii_http_body.txt"));
+ builder.SetFileAttachment("minidump",
+ "minidump.dmp",
+ ascii_http_body_path,
+ "");
+ const char kValue2[] = "this is not a file";
+ builder.SetFormData("minidump", kValue2);
+
+ scoped_ptr<HTTPBodyStream> body(builder.GetBodyStream());
+ ASSERT_TRUE(body.get());
+ std::string contents = ReadStreamToString(body.get());
+ auto lines = SplitCRLF(contents);
+ auto lines_it = lines.begin();
+
+ const std::string& boundary = *lines_it++;
+ EXPECT_GE(boundary.length(), 1u);
+ EXPECT_LE(boundary.length(), 70u);
+
+ EXPECT_EQ("Content-Disposition: form-data; name=\"minidump\"", *lines_it++);
+ EXPECT_EQ("", *lines_it++);
+ EXPECT_EQ(kValue2, *lines_it++);
+
+ EXPECT_EQ(boundary, *lines_it++);
+ EXPECT_EQ("Content-Disposition: form-data; name=\"one\"", *lines_it++);
+ EXPECT_EQ("", *lines_it++);
+ EXPECT_EQ(kValue1, *lines_it++);
+
+ EXPECT_EQ(boundary + "--", *lines_it++);
+
+ EXPECT_EQ(lines.end(), lines_it);
+}
+
+TEST(HTTPMultipartBuilderDeathTest, AssertUnsafeMIMEType) {
+ HTTPMultipartBuilder builder;
+ // Invalid and potentially dangerous:
+ ASSERT_DEATH_CHECK(
+ builder.SetFileAttachment("", "", base::FilePath(), "\r\n"), "");
+ ASSERT_DEATH_CHECK(
+ builder.SetFileAttachment("", "", base::FilePath(), "\""), "");
+ ASSERT_DEATH_CHECK(
+ builder.SetFileAttachment("", "", base::FilePath(), "\x12"), "");
+ ASSERT_DEATH_CHECK(
+ builder.SetFileAttachment("", "", base::FilePath(), "<>"), "");
+ // Invalid but safe:
+ builder.SetFileAttachment("", "", base::FilePath(), "0/totally/-invalid.pdf");
+ // Valid and safe:
+ builder.SetFileAttachment("", "", base::FilePath(), "application/xml+xhtml");
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/net/http_transport.cc b/chromium/third_party/crashpad/crashpad/util/net/http_transport.cc
new file mode 100644
index 00000000000..f11ee95c32d
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/net/http_transport.cc
@@ -0,0 +1,53 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/net/http_transport.h"
+
+#include "util/net/http_body.h"
+
+namespace crashpad {
+
+HTTPTransport::HTTPTransport()
+ : url_(),
+ method_("POST"),
+ headers_(),
+ body_stream_(),
+ timeout_(15.0) {
+}
+
+HTTPTransport::~HTTPTransport() {
+}
+
+void HTTPTransport::SetURL(const std::string& url) {
+ url_ = url;
+}
+
+void HTTPTransport::SetMethod(const std::string& method) {
+ method_ = method;
+}
+
+void HTTPTransport::SetHeader(const std::string& header,
+ const std::string& value) {
+ headers_[header] = value;
+}
+
+void HTTPTransport::SetBodyStream(scoped_ptr<HTTPBodyStream> stream) {
+ body_stream_ = stream.Pass();
+}
+
+void HTTPTransport::SetTimeout(double timeout) {
+ timeout_ = timeout;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/net/http_transport.h b/chromium/third_party/crashpad/crashpad/util/net/http_transport.h
new file mode 100644
index 00000000000..333986a9772
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/net/http_transport.h
@@ -0,0 +1,106 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_NET_HTTP_TRANSPORT_H_
+#define CRASHPAD_UTIL_NET_HTTP_TRANSPORT_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "util/net/http_headers.h"
+
+namespace crashpad {
+
+class HTTPBodyStream;
+
+//! \brief HTTPTransport executes a HTTP request using the specified URL, HTTP
+//! method, headers, and body. This class can only issue a synchronous
+//! HTTP request.
+//!
+//! This class cannot be instantiated directly. A concrete subclass must be
+//! instantiated instead, which provides an implementation to execute the
+//! request that is appropriate for the host operating system.
+class HTTPTransport {
+ public:
+ virtual ~HTTPTransport();
+
+ //! \brief Instantiates a concrete HTTPTransport class for the current
+ //! operating system.
+ //!
+ //! \return A new caller-owned HTTPTransport object.
+ static scoped_ptr<HTTPTransport> Create();
+
+ //! \brief Sets URL to which the request will be made.
+ //!
+ //! \param[in] url The request URL.
+ void SetURL(const std::string& url);
+
+ //! \brief Sets the HTTP method to execute. E.g., GET, POST, etc. The default
+ //! method is `"POST"`.
+ //!
+ //! \param[in] http_method The HTTP method.
+ void SetMethod(const std::string& http_method);
+
+ //! \brief Sets a HTTP header-value pair.
+ //!
+ //! \param[in] header The HTTP header name. Any previous value set at this
+ //! name will be overwritten.
+ //! \param[in] value The value to set for the header.
+ void SetHeader(const std::string& header, const std::string& value);
+
+ //! \brief Sets the stream object from which to generate the HTTP body.
+ //!
+ //! \param[in] stream A HTTPBodyStream, of which this class will take
+ //! ownership.
+ void SetBodyStream(scoped_ptr<HTTPBodyStream> stream);
+
+ //! \brief Sets the timeout for the HTTP request. The default is 15 seconds.
+ //!
+ //! \param[in] timeout The request timeout, in seconds.
+ void SetTimeout(double timeout);
+
+ //! \brief Performs the HTTP request with the configured parameters and waits
+ //! for the execution to complete.
+ //!
+ //! \param[out] response On success, this will be set to the HTTP response
+ //! body. This parameter is optional and may be set to `nullptr` if the
+ //! response body is not required.
+ //!
+ //! \return Whether or not the request was successful, defined as returning
+ //! a HTTP status 200 (OK) code.
+ virtual bool ExecuteSynchronously(std::string* response_body) = 0;
+
+ protected:
+ HTTPTransport();
+
+ const std::string& url() const { return url_; }
+ const std::string& method() const { return method_; }
+ const HTTPHeaders& headers() const { return headers_; }
+ HTTPBodyStream* body_stream() const { return body_stream_.get(); }
+ double timeout() const { return timeout_; }
+
+ private:
+ std::string url_;
+ std::string method_;
+ HTTPHeaders headers_;
+ scoped_ptr<HTTPBodyStream> body_stream_;
+ double timeout_;
+
+ DISALLOW_COPY_AND_ASSIGN(HTTPTransport);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_NET_HTTP_TRANSPORT_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/net/http_transport_mac.mm b/chromium/third_party/crashpad/crashpad/util/net/http_transport_mac.mm
new file mode 100644
index 00000000000..9688d18cbab
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/net/http_transport_mac.mm
@@ -0,0 +1,223 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/net/http_transport.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#import <Foundation/Foundation.h>
+
+#include "base/mac/foundation_util.h"
+#import "base/mac/scoped_nsobject.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/sys_string_conversions.h"
+#include "third_party/apple_cf/CFStreamAbstract.h"
+#include "util/net/http_body.h"
+
+namespace crashpad {
+
+namespace {
+
+// An implementation of CFReadStream. This implements the V0 callback
+// scheme.
+class HTTPBodyStreamCFReadStream {
+ public:
+ explicit HTTPBodyStreamCFReadStream(HTTPBodyStream* body_stream)
+ : body_stream_(body_stream) {
+ }
+
+ // Creates a new NSInputStream, which the caller owns.
+ NSInputStream* CreateInputStream() {
+ CFStreamClientContext context = {
+ .version = 0,
+ .info = this,
+ .retain = nullptr,
+ .release = nullptr,
+ .copyDescription = nullptr
+ };
+ const CFReadStreamCallBacksV0 callbacks = {
+ .version = 0,
+ .open = &Open,
+ .openCompleted = &OpenCompleted,
+ .read = &Read,
+ .getBuffer = &GetBuffer,
+ .canRead = &CanRead,
+ .close = &Close,
+ .copyProperty = &CopyProperty,
+ .schedule = &Schedule,
+ .unschedule = &Unschedule
+ };
+ CFReadStreamRef read_stream = CFReadStreamCreate(nullptr,
+ reinterpret_cast<const CFReadStreamCallBacks*>(&callbacks), &context);
+ return base::mac::CFToNSCast(read_stream);
+ }
+
+ private:
+ static HTTPBodyStream* GetStream(void* info) {
+ return static_cast<HTTPBodyStreamCFReadStream*>(info)->body_stream_;
+ }
+
+ static Boolean Open(CFReadStreamRef stream,
+ CFStreamError* error,
+ Boolean* open_complete,
+ void* info) {
+ *open_complete = TRUE;
+ return TRUE;
+ }
+
+ static Boolean OpenCompleted(CFReadStreamRef stream,
+ CFStreamError* error,
+ void* info) {
+ return TRUE;
+ }
+
+ static CFIndex Read(CFReadStreamRef stream,
+ UInt8* buffer,
+ CFIndex buffer_length,
+ CFStreamError* error,
+ Boolean* at_eof,
+ void* info) {
+ if (buffer_length == 0)
+ return 0;
+
+ ssize_t bytes_read = GetStream(info)->GetBytesBuffer(buffer, buffer_length);
+ if (bytes_read == 0) {
+ *at_eof = TRUE;
+ } else if (bytes_read < 0) {
+ error->error = -1;
+ error->domain = kCFStreamErrorDomainCustom;
+ }
+
+ return bytes_read;
+ }
+
+ static const UInt8* GetBuffer(CFReadStreamRef stream,
+ CFIndex max_bytes_to_read,
+ CFIndex* num_bytes_read,
+ CFStreamError* error,
+ Boolean* at_eof,
+ void* info) {
+ return nullptr;
+ }
+
+ static Boolean CanRead(CFReadStreamRef stream, void* info) {
+ return TRUE;
+ }
+
+ static void Close(CFReadStreamRef stream, void* info) {}
+
+ static CFTypeRef CopyProperty(CFReadStreamRef stream,
+ CFStringRef property_name,
+ void* info) {
+ return nullptr;
+ }
+
+ static void Schedule(CFReadStreamRef stream,
+ CFRunLoopRef run_loop,
+ CFStringRef run_loop_mode,
+ void* info) {}
+
+ static void Unschedule(CFReadStreamRef stream,
+ CFRunLoopRef run_loop,
+ CFStringRef run_loop_mode,
+ void* info) {}
+
+ HTTPBodyStream* body_stream_; // weak
+
+ DISALLOW_COPY_AND_ASSIGN(HTTPBodyStreamCFReadStream);
+};
+
+class HTTPTransportMac final : public HTTPTransport {
+ public:
+ HTTPTransportMac();
+ ~HTTPTransportMac() override;
+
+ bool ExecuteSynchronously(std::string* response_body) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HTTPTransportMac);
+};
+
+HTTPTransportMac::HTTPTransportMac() : HTTPTransport() {
+}
+
+HTTPTransportMac::~HTTPTransportMac() {
+}
+
+bool HTTPTransportMac::ExecuteSynchronously(std::string* response_body) {
+ DCHECK(body_stream());
+
+ @autoreleasepool {
+ NSString* url_ns_string = base::SysUTF8ToNSString(url());
+ NSURL* url = [NSURL URLWithString:url_ns_string];
+ NSMutableURLRequest* request =
+ [NSMutableURLRequest requestWithURL:url
+ cachePolicy:NSURLRequestUseProtocolCachePolicy
+ timeoutInterval:timeout()];
+ [request setHTTPMethod:base::SysUTF8ToNSString(method())];
+
+ for (const auto& pair : headers()) {
+ [request setValue:base::SysUTF8ToNSString(pair.second)
+ forHTTPHeaderField:base::SysUTF8ToNSString(pair.first)];
+ }
+
+ HTTPBodyStreamCFReadStream body_stream_cf(body_stream());
+ base::scoped_nsobject<NSInputStream> input_stream(
+ body_stream_cf.CreateInputStream());
+ [request setHTTPBodyStream:input_stream.get()];
+
+ NSURLResponse* response = nil;
+ NSError* error = nil;
+ NSData* body = [NSURLConnection sendSynchronousRequest:request
+ returningResponse:&response
+ error:&error];
+
+ if (error) {
+ LOG(ERROR) << [[error localizedDescription] UTF8String] << " ("
+ << [[error domain] UTF8String] << " " << [error code] << ")";
+ return false;
+ }
+ if (!response) {
+ LOG(ERROR) << "no response";
+ return false;
+ }
+ NSHTTPURLResponse* http_response =
+ base::mac::ObjCCast<NSHTTPURLResponse>(response);
+ if (!http_response) {
+ LOG(ERROR) << "no http_response";
+ return false;
+ }
+ NSInteger http_status = [http_response statusCode];
+ if (http_status != 200) {
+ LOG(ERROR) << base::StringPrintf("HTTP status %ld",
+ implicit_cast<long>(http_status));
+ return false;
+ }
+
+ if (response_body) {
+ response_body->assign(static_cast<const char*>([body bytes]),
+ [body length]);
+ }
+
+ return true;
+ }
+}
+
+} // namespace
+
+// static
+scoped_ptr<HTTPTransport> HTTPTransport::Create() {
+ return scoped_ptr<HTTPTransport>(new HTTPTransportMac());
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/net/http_transport_test.cc b/chromium/third_party/crashpad/crashpad/util/net/http_transport_test.cc
new file mode 100644
index 00000000000..dc3775b11fd
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/net/http_transport_test.cc
@@ -0,0 +1,283 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/net/http_transport.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/format_macros.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/rand_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "gtest/gtest.h"
+#include "test/multiprocess_exec.h"
+#include "test/paths.h"
+#include "util/file/file_io.h"
+#include "util/net/http_body.h"
+#include "util/net/http_headers.h"
+#include "util/net/http_multipart_builder.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+class HTTPTransportTestFixture : public MultiprocessExec {
+ public:
+ using RequestValidator =
+ void(*)(HTTPTransportTestFixture*, const std::string&);
+
+ HTTPTransportTestFixture(const HTTPHeaders& headers,
+ scoped_ptr<HTTPBodyStream> body_stream,
+ uint16_t http_response_code,
+ RequestValidator request_validator)
+ : MultiprocessExec(),
+ headers_(headers),
+ body_stream_(body_stream.Pass()),
+ response_code_(http_response_code),
+ request_validator_(request_validator) {
+ base::FilePath server_path = Paths::TestDataRoot().Append(
+ FILE_PATH_LITERAL("util/net/http_transport_test_server.py"));
+#if defined(OS_POSIX)
+ SetChildCommand(server_path.value(), nullptr);
+#elif defined(OS_WIN)
+ // Explicitly invoke a shell and python so that python can be found in the
+ // path, and run the test script.
+ std::vector<std::string> args;
+ args.push_back("/c");
+ args.push_back("python");
+ args.push_back(base::UTF16ToUTF8(server_path.value()));
+ SetChildCommand(getenv("COMSPEC"), &args);
+#endif // OS_POSIX
+ }
+
+ const HTTPHeaders& headers() { return headers_; }
+
+ private:
+ void MultiprocessParent() override {
+ // Use Logging*File() instead of Checked*File() so that the test can fail
+ // gracefully with a gtest assertion if the child does not execute properly.
+
+ // The child will write the HTTP server port number as a packed unsigned
+ // short to stdout.
+ uint16_t port;
+ ASSERT_TRUE(LoggingReadFile(ReadPipeHandle(), &port, sizeof(port)));
+
+ // Then the parent will tell the web server what response code to send
+ // for the HTTP request.
+ ASSERT_TRUE(LoggingWriteFile(
+ WritePipeHandle(), &response_code_, sizeof(response_code_)));
+
+ // The parent will also tell the web server what response body to send back.
+ // The web server will only send the response body if the response code is
+ // 200.
+ std::string expect_response_body;
+ for (size_t index = 0; index < 8; ++index) {
+ expect_response_body += static_cast<char>(base::RandInt(' ', '~'));
+ }
+
+ ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(),
+ expect_response_body.c_str(),
+ expect_response_body.size()));
+
+ // Now execute the HTTP request.
+ scoped_ptr<HTTPTransport> transport(HTTPTransport::Create());
+ transport->SetMethod("POST");
+ transport->SetURL(base::StringPrintf("http://127.0.0.1:%d/upload", port));
+ for (const auto& pair : headers_) {
+ transport->SetHeader(pair.first, pair.second);
+ }
+ transport->SetBodyStream(body_stream_.Pass());
+
+ std::string response_body;
+ bool success = transport->ExecuteSynchronously(&response_body);
+ if (response_code_ == 200) {
+ EXPECT_TRUE(success);
+ expect_response_body += "\r\n";
+ EXPECT_EQ(expect_response_body, response_body);
+ } else {
+ EXPECT_FALSE(success);
+ EXPECT_TRUE(response_body.empty());
+ }
+
+ // Read until the child's stdout closes.
+ std::string request;
+ char buf[32];
+ ssize_t bytes_read;
+ while ((bytes_read = ReadFile(ReadPipeHandle(), buf, sizeof(buf))) != 0) {
+ ASSERT_GE(bytes_read, 0);
+ request.append(buf, bytes_read);
+ }
+
+ if (request_validator_)
+ request_validator_(this, request);
+ }
+
+ HTTPHeaders headers_;
+ scoped_ptr<HTTPBodyStream> body_stream_;
+ uint16_t response_code_;
+ RequestValidator request_validator_;
+};
+
+const char kMultipartFormData[] = "multipart/form-data";
+
+void GetHeaderField(const std::string& request,
+ const std::string& header,
+ std::string* value) {
+ size_t index = request.find(header);
+ ASSERT_NE(std::string::npos, index);
+ // Since the header is never the first line of the request, it should always
+ // be preceded by a CRLF.
+ EXPECT_EQ('\n', request[index - 1]);
+ EXPECT_EQ('\r', request[index - 2]);
+
+ index += header.length();
+ EXPECT_EQ(':', request[index++]);
+ // Per RFC 7230 §3.2, there can be one or more spaces or horizontal tabs.
+ // For testing purposes, just assume one space.
+ EXPECT_EQ(' ', request[index++]);
+
+ size_t header_end = request.find('\r', index);
+ ASSERT_NE(std::string::npos, header_end);
+
+ *value = request.substr(index, header_end - index);
+}
+
+void GetMultipartBoundary(const std::string& request,
+ std::string* multipart_boundary) {
+ std::string content_type;
+ GetHeaderField(request, kContentType, &content_type);
+
+ ASSERT_GE(content_type.length(), strlen(kMultipartFormData));
+ size_t index = strlen(kMultipartFormData);
+ EXPECT_EQ(kMultipartFormData, content_type.substr(0, index));
+
+ EXPECT_EQ(';', content_type[index++]);
+
+ size_t boundary_begin = content_type.find('=', index);
+ ASSERT_NE(std::string::npos, boundary_begin);
+ EXPECT_EQ('=', content_type[boundary_begin++]);
+ if (multipart_boundary) {
+ *multipart_boundary = content_type.substr(boundary_begin);
+ }
+}
+
+const char kBoundaryEq[] = "boundary=";
+
+void ValidFormData(HTTPTransportTestFixture* fixture,
+ const std::string& request) {
+ std::string actual_boundary;
+ GetMultipartBoundary(request, &actual_boundary);
+
+ const auto& content_type = fixture->headers().find(kContentType);
+ ASSERT_NE(fixture->headers().end(), content_type);
+
+ size_t boundary = content_type->second.find(kBoundaryEq);
+ ASSERT_NE(std::string::npos, boundary);
+ std::string expected_boundary =
+ content_type->second.substr(boundary + strlen(kBoundaryEq));
+ EXPECT_EQ(expected_boundary, actual_boundary);
+
+ size_t body_start = request.find("\r\n\r\n");
+ ASSERT_NE(std::string::npos, body_start);
+ body_start += 4;
+
+ std::string expected = "--" + expected_boundary + "\r\n";
+ expected += "Content-Disposition: form-data; name=\"key1\"\r\n\r\n";
+ expected += "test\r\n";
+ ASSERT_LT(body_start + expected.length(), request.length());
+ EXPECT_EQ(expected, request.substr(body_start, expected.length()));
+
+ body_start += expected.length();
+
+ expected = "--" + expected_boundary + "\r\n";
+ expected += "Content-Disposition: form-data; name=\"key2\"\r\n\r\n";
+ expected += "--abcdefg123\r\n";
+ expected += "--" + expected_boundary + "--\r\n";
+ ASSERT_EQ(body_start + expected.length(), request.length());
+ EXPECT_EQ(expected, request.substr(body_start));
+}
+
+TEST(HTTPTransport, ValidFormData) {
+ HTTPMultipartBuilder builder;
+ builder.SetFormData("key1", "test");
+ builder.SetFormData("key2", "--abcdefg123");
+
+ HTTPHeaders headers;
+ EXPECT_TRUE(headers.insert(builder.GetContentType()).second);
+
+ HTTPTransportTestFixture test(headers, builder.GetBodyStream(), 200,
+ &ValidFormData);
+ test.Run();
+}
+
+const char kTextPlain[] = "text/plain";
+
+void ErrorResponse(HTTPTransportTestFixture* fixture,
+ const std::string& request) {
+ std::string content_type;
+ GetHeaderField(request, kContentType, &content_type);
+ EXPECT_EQ(kTextPlain, content_type);
+}
+
+TEST(HTTPTransport, ErrorResponse) {
+ HTTPMultipartBuilder builder;
+ HTTPHeaders headers;
+ headers[kContentType] = kTextPlain;
+ HTTPTransportTestFixture test(headers, builder.GetBodyStream(),
+ 404, &ErrorResponse);
+ test.Run();
+}
+
+const char kTextBody[] = "hello world";
+
+void UnchunkedPlainText(HTTPTransportTestFixture* fixture,
+ const std::string& request) {
+ std::string header_value;
+ GetHeaderField(request, kContentType, &header_value);
+ EXPECT_EQ(kTextPlain, header_value);
+
+ GetHeaderField(request, kContentLength, &header_value);
+ const auto& content_length = fixture->headers().find(kContentLength);
+ ASSERT_NE(fixture->headers().end(), content_length);
+ EXPECT_EQ(content_length->second, header_value);
+
+ size_t body_start = request.rfind("\r\n");
+ ASSERT_NE(std::string::npos, body_start);
+
+ EXPECT_EQ(kTextBody, request.substr(body_start + 2));
+}
+
+TEST(HTTPTransport, UnchunkedPlainText) {
+ scoped_ptr<HTTPBodyStream> body_stream(new StringHTTPBodyStream(kTextBody));
+
+ HTTPHeaders headers;
+ headers[kContentType] = kTextPlain;
+ headers[kContentLength] = base::StringPrintf("%" PRIuS, strlen(kTextBody));
+
+ HTTPTransportTestFixture test(headers, body_stream.Pass(), 200,
+ &UnchunkedPlainText);
+ test.Run();
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/net/http_transport_test_server.py b/chromium/third_party/crashpad/crashpad/util/net/http_transport_test_server.py
new file mode 100755
index 00000000000..ecf71d702fe
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/net/http_transport_test_server.py
@@ -0,0 +1,150 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""A one-shot testing webserver.
+
+When invoked, this server will write a short integer to stdout, indiciating on
+which port the server is listening. It will then read one integer from stdin,
+indiciating the response code to be sent in response to a request. It also reads
+8 characters from stdin, which, after having "\r\n" appended, will form the
+response body in a successful response (one with code 200). The server will
+process one HTTP request, deliver the prearranged response to the client, and
+write the entire request to stdout. It will then terminate.
+
+This server is written in Python since it provides a simple HTTP stack, and
+because parsing Chunked encoding is safer and easier in a memory-safe language.
+This could easily have been written in C++ instead.
+"""
+
+import BaseHTTPServer
+import struct
+import sys
+
+class BufferedReadFile(object):
+ """A File-like object that stores all read contents into a buffer."""
+
+ def __init__(self, real_file):
+ self.file = real_file
+ self.buffer = ""
+
+ def read(self, size=-1):
+ buf = self.file.read(size)
+ self.buffer += buf
+ return buf
+
+ def readline(self, size=-1):
+ buf = self.file.readline(size)
+ self.buffer += buf
+ return buf
+
+ def flush(self):
+ self.file.flush()
+
+ def close(self):
+ self.file.close()
+
+
+class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+ response_code = 500
+ response_body = ''
+
+ def handle_one_request(self):
+ # Wrap the rfile in the buffering file object so that the raw header block
+ # can be written to stdout after it is parsed.
+ self.rfile = BufferedReadFile(self.rfile)
+ BaseHTTPServer.BaseHTTPRequestHandler.handle_one_request(self)
+
+ def do_POST(self):
+ writer = sys.stdout
+
+ writer.write(self.rfile.buffer)
+ self.rfile.buffer = ''
+
+ if self.headers.get('Transfer-Encoding', '') == 'Chunked':
+ body = self.handle_chunked_encoding()
+ else:
+ length = int(self.headers.get('Content-Length', -1))
+ body = self.rfile.read(length)
+
+ self.send_response(self.response_code)
+ self.end_headers()
+ if self.response_code == 200:
+ self.wfile.write(self.response_body)
+ self.wfile.write('\r\n')
+
+ writer.write(body)
+ writer.flush()
+
+ def handle_chunked_encoding(self):
+ """This parses a "Transfer-Encoding: Chunked" body in accordance with
+ RFC 7230 §4.1. This returns the result as a string.
+ """
+ body = ''
+ chunk_size = self.read_chunk_size()
+ while chunk_size > 0:
+ # Read the body.
+ data = self.rfile.read(chunk_size)
+ chunk_size -= len(data)
+ body += data
+
+ # Finished reading this chunk.
+ if chunk_size == 0:
+ # Read through any trailer fields.
+ trailer_line = self.rfile.readline()
+ while trailer_line.strip() != '':
+ trailer_line = self.rfile.readline()
+
+ # Read the chunk size.
+ chunk_size = self.read_chunk_size()
+ return body
+
+ def read_chunk_size(self):
+ # Read the whole line, including the \r\n.
+ chunk_size_and_ext_line = self.rfile.readline()
+ # Look for a chunk extension.
+ chunk_size_end = chunk_size_and_ext_line.find(';')
+ if chunk_size_end == -1:
+ # No chunk extensions; just encounter the end of line.
+ chunk_size_end = chunk_size_and_ext_line.find('\r')
+ if chunk_size_end == -1:
+ self.send_response(400) # Bad request.
+ return -1
+ return int(chunk_size_and_ext_line[:chunk_size_end], base=16)
+
+
+def Main():
+ if sys.platform == 'win32':
+ import os, msvcrt
+ msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
+
+ # Start the server.
+ server = BaseHTTPServer.HTTPServer(('127.0.0.1', 0), RequestHandler)
+
+ # Write the port as an unsigned short to the parent process.
+ sys.stdout.write(struct.pack('=H', server.server_address[1]))
+ sys.stdout.flush()
+
+ # Read the desired test response code as an unsigned short and the desired
+ # response body as an 8-byte string from the parent process.
+ RequestHandler.response_code, RequestHandler.response_body = \
+ struct.unpack('=H8s', sys.stdin.read(struct.calcsize('=H8s')))
+
+ # Handle the request.
+ server.handle_request()
+
+if __name__ == '__main__':
+ Main()
diff --git a/chromium/third_party/crashpad/crashpad/util/net/http_transport_win.cc b/chromium/third_party/crashpad/crashpad/util/net/http_transport_win.cc
new file mode 100644
index 00000000000..2b6e372f50f
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/net/http_transport_win.cc
@@ -0,0 +1,244 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/net/http_transport.h"
+
+#include <windows.h>
+#include <winhttp.h>
+
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/scoped_generic.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "util/net/http_body.h"
+#include "package.h"
+
+namespace crashpad {
+
+namespace {
+
+// PLOG doesn't work for messages from WinHTTP, so we need to use
+// FORMAT_MESSAGE_FROM_HMODULE + the dll name manually here.
+void LogErrorWinHttpMessage(const char* extra) {
+ DWORD error_code = GetLastError();
+ char msgbuf[256];
+ DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
+ FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_HMODULE;
+ DWORD len = FormatMessageA(flags,
+ GetModuleHandle(L"winhttp.dll"),
+ error_code,
+ 0,
+ msgbuf,
+ arraysize(msgbuf),
+ NULL);
+ if (len) {
+ LOG(ERROR) << extra << ": " << msgbuf
+ << base::StringPrintf(" (0x%X)", error_code);
+ } else {
+ LOG(ERROR) << base::StringPrintf(
+ "Error (0x%X) while retrieving error. (0x%X)",
+ GetLastError(),
+ error_code);
+ }
+}
+
+struct ScopedHINTERNETTraits {
+ static HINTERNET InvalidValue() {
+ return nullptr;
+ }
+ static void Free(HINTERNET handle) {
+ if (handle) {
+ if (!WinHttpCloseHandle(handle)) {
+ LogErrorWinHttpMessage("WinHttpCloseHandle");
+ }
+ }
+ }
+};
+
+using ScopedHINTERNET = base::ScopedGeneric<HINTERNET, ScopedHINTERNETTraits>;
+
+class HTTPTransportWin final : public HTTPTransport {
+ public:
+ HTTPTransportWin();
+ ~HTTPTransportWin() override;
+
+ bool ExecuteSynchronously(std::string* response_body) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HTTPTransportWin);
+};
+
+HTTPTransportWin::HTTPTransportWin() : HTTPTransport() {
+}
+
+HTTPTransportWin::~HTTPTransportWin() {
+}
+
+bool HTTPTransportWin::ExecuteSynchronously(std::string* response_body) {
+ ScopedHINTERNET session(
+ WinHttpOpen(base::UTF8ToUTF16(PACKAGE_NAME "/" PACKAGE_VERSION).c_str(),
+ WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
+ WINHTTP_NO_PROXY_NAME,
+ WINHTTP_NO_PROXY_BYPASS,
+ 0));
+ if (!session.get()) {
+ LogErrorWinHttpMessage("WinHttpOpen");
+ return false;
+ }
+
+ int timeout_in_ms = static_cast<int>(timeout() * 1000);
+ if (!WinHttpSetTimeouts(session.get(),
+ timeout_in_ms,
+ timeout_in_ms,
+ timeout_in_ms,
+ timeout_in_ms)) {
+ LogErrorWinHttpMessage("WinHttpSetTimeouts");
+ return false;
+ }
+
+ URL_COMPONENTS url_components = {0};
+ url_components.dwStructSize = sizeof(URL_COMPONENTS);
+ url_components.dwHostNameLength = 1;
+ url_components.dwUrlPathLength = 1;
+ url_components.dwExtraInfoLength = 1;
+ std::wstring url_wide(base::UTF8ToUTF16(url()));
+ if (!WinHttpCrackUrl(
+ url_wide.c_str(), 0, ICU_REJECT_USERPWD, &url_components)) {
+ LogErrorWinHttpMessage("WinHttpCrackUrl");
+ return false;
+ }
+ std::wstring host_name(url_components.lpszHostName,
+ url_components.dwHostNameLength);
+ std::wstring url_path(url_components.lpszUrlPath,
+ url_components.dwUrlPathLength);
+ std::wstring extra_info(url_components.lpszExtraInfo,
+ url_components.dwExtraInfoLength);
+
+ ScopedHINTERNET connect(WinHttpConnect(
+ session.get(), host_name.c_str(), url_components.nPort, 0));
+ if (!connect.get()) {
+ LogErrorWinHttpMessage("WinHttpConnect");
+ return false;
+ }
+
+ ScopedHINTERNET request(
+ WinHttpOpenRequest(connect.get(),
+ base::UTF8ToUTF16(method()).c_str(),
+ url_path.c_str(),
+ nullptr,
+ WINHTTP_NO_REFERER,
+ WINHTTP_DEFAULT_ACCEPT_TYPES,
+ 0));
+ if (!request.get()) {
+ LogErrorWinHttpMessage("WinHttpOpenRequest");
+ return false;
+ }
+
+ // Add headers to the request.
+ for (const auto& pair : headers()) {
+ std::wstring header_string =
+ base::UTF8ToUTF16(pair.first) + L": " + base::UTF8ToUTF16(pair.second);
+ if (!WinHttpAddRequestHeaders(
+ request.get(),
+ header_string.c_str(),
+ base::checked_cast<DWORD>(header_string.size()),
+ WINHTTP_ADDREQ_FLAG_ADD)) {
+ LogErrorWinHttpMessage("WinHttpAddRequestHeaders");
+ return false;
+ }
+ }
+
+ // We need the Content-Length up front, so buffer in memory. We should modify
+ // the interface to not require this, and then use WinHttpWriteData after
+ // WinHttpSendRequest.
+ std::vector<uint8_t> post_data;
+
+ // Write the body of a POST if any.
+ const size_t kBufferSize = 4096;
+ for (;;) {
+ uint8_t buffer[kBufferSize];
+ ssize_t bytes_to_write =
+ body_stream()->GetBytesBuffer(buffer, sizeof(buffer));
+ if (bytes_to_write == 0)
+ break;
+ post_data.insert(post_data.end(), buffer, buffer + bytes_to_write);
+ }
+
+ if (!WinHttpSendRequest(request.get(),
+ WINHTTP_NO_ADDITIONAL_HEADERS,
+ 0,
+ &post_data[0],
+ base::checked_cast<DWORD>(post_data.size()),
+ base::checked_cast<DWORD>(post_data.size()),
+ 0)) {
+ LogErrorWinHttpMessage("WinHttpSendRequest");
+ return false;
+ }
+
+ if (!WinHttpReceiveResponse(request.get(), nullptr)) {
+ LogErrorWinHttpMessage("WinHttpReceiveResponse");
+ return false;
+ }
+
+ DWORD status_code = 0;
+ DWORD sizeof_status_code = sizeof(status_code);
+
+ if (!WinHttpQueryHeaders(
+ request.get(),
+ WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
+ WINHTTP_HEADER_NAME_BY_INDEX,
+ &status_code,
+ &sizeof_status_code,
+ WINHTTP_NO_HEADER_INDEX)) {
+ LogErrorWinHttpMessage("WinHttpQueryHeaders");
+ return false;
+ }
+
+ if (status_code != 200) {
+ LOG(ERROR) << base::StringPrintf("HTTP status %d", status_code);
+ return false;
+ }
+
+ if (response_body) {
+ response_body->clear();
+
+ // There isn’t any reason to call WinHttpQueryDataAvailable(), because it
+ // returns the number of bytes available to be read without blocking at the
+ // time of the call, not the number of bytes until end-of-file. This method,
+ // which executes synchronously, is only concerned with reading until EOF.
+ DWORD bytes_read = 0;
+ do {
+ char read_buffer[kBufferSize];
+ if (!WinHttpReadData(
+ request.get(), read_buffer, sizeof(read_buffer), &bytes_read)) {
+ LogErrorWinHttpMessage("WinHttpReadData");
+ return false;
+ }
+
+ response_body->append(read_buffer, bytes_read);
+ } while (bytes_read > 0);
+ }
+
+ return true;
+}
+
+} // namespace
+
+// static
+scoped_ptr<HTTPTransport> HTTPTransport::Create() {
+ return scoped_ptr<HTTPTransportWin>(new HTTPTransportWin);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/numeric/checked_address_range.cc b/chromium/third_party/crashpad/crashpad/util/numeric/checked_address_range.cc
new file mode 100644
index 00000000000..dcbde23e72e
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/numeric/checked_address_range.cc
@@ -0,0 +1,120 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/numeric/checked_address_range.h"
+
+#if defined(OS_MACOSX)
+#include <mach/mach.h>
+#elif defined(OS_WIN)
+#include "util/win/address_types.h"
+#endif // OS_MACOSX
+
+namespace crashpad {
+namespace internal {
+
+template <class ValueType, class SizeType>
+CheckedAddressRangeGeneric<ValueType, SizeType>::CheckedAddressRangeGeneric()
+ : range_32_(0, 0),
+#if defined(COMPILER_MSVC)
+ range_64_(0, 0),
+#endif // COMPILER_MSVC
+ is_64_bit_(false),
+ range_ok_(true) {
+}
+
+template <class ValueType, class SizeType>
+CheckedAddressRangeGeneric<ValueType, SizeType>::CheckedAddressRangeGeneric(
+ bool is_64_bit,
+ ValueType base,
+ SizeType size)
+#if defined(COMPILER_MSVC)
+ : range_32_(0, 0),
+ range_64_(0, 0)
+#endif // COMPILER_MSVC
+{
+ SetRange(is_64_bit, base, size);
+}
+
+template <class ValueType, class SizeType>
+ValueType CheckedAddressRangeGeneric<ValueType, SizeType>::Base() const {
+ return is_64_bit_ ? range_64_.base() : range_32_.base();
+}
+
+template <class ValueType, class SizeType>
+SizeType CheckedAddressRangeGeneric<ValueType, SizeType>::Size() const {
+ return is_64_bit_ ? range_64_.size() : range_32_.size();
+}
+
+template <class ValueType, class SizeType>
+ValueType CheckedAddressRangeGeneric<ValueType, SizeType>::End() const {
+ return is_64_bit_ ? range_64_.end() : range_32_.end();
+}
+
+template <class ValueType, class SizeType>
+bool CheckedAddressRangeGeneric<ValueType, SizeType>::IsValid() const {
+ return range_ok_ && (is_64_bit_ ? range_64_.IsValid() : range_32_.IsValid());
+}
+
+template <class ValueType, class SizeType>
+void CheckedAddressRangeGeneric<ValueType, SizeType>::SetRange(bool is_64_bit,
+ ValueType base,
+ SizeType size) {
+ is_64_bit_ = is_64_bit;
+ if (is_64_bit_) {
+ range_64_.SetRange(base, size);
+ range_ok_ = true;
+ } else {
+ range_32_.SetRange(static_cast<uint32_t>(base),
+ static_cast<uint32_t>(size));
+ range_ok_ = base::IsValueInRangeForNumericType<uint32_t>(base) &&
+ base::IsValueInRangeForNumericType<uint32_t>(size);
+ }
+}
+
+template <class ValueType, class SizeType>
+bool CheckedAddressRangeGeneric<ValueType, SizeType>::ContainsValue(
+ ValueType value) const {
+ DCHECK(range_ok_);
+
+ if (is_64_bit_) {
+ return range_64_.ContainsValue(value);
+ }
+
+ if (!base::IsValueInRangeForNumericType<uint32_t>(value)) {
+ return false;
+ }
+
+ return range_32_.ContainsValue(static_cast<uint32_t>(value));
+}
+
+template <class ValueType, class SizeType>
+bool CheckedAddressRangeGeneric<ValueType, SizeType>::ContainsRange(
+ const CheckedAddressRangeGeneric& that) const {
+ DCHECK_EQ(is_64_bit_, that.is_64_bit_);
+ DCHECK(range_ok_);
+ DCHECK(that.range_ok_);
+
+ return is_64_bit_ ? range_64_.ContainsRange(that.range_64_)
+ : range_32_.ContainsRange(that.range_32_);
+}
+
+// Explicit instantiations for the cases we use.
+#if defined(OS_MACOSX)
+template class CheckedAddressRangeGeneric<mach_vm_address_t, mach_vm_size_t>;
+#elif defined(OS_WIN)
+template class CheckedAddressRangeGeneric<WinVMAddress, WinVMSize>;
+#endif // OS_MACOSX
+
+} // namespace internal
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/numeric/checked_address_range.h b/chromium/third_party/crashpad/crashpad/util/numeric/checked_address_range.h
new file mode 100644
index 00000000000..e9514bbdec6
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/numeric/checked_address_range.h
@@ -0,0 +1,144 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_NUMERIC_CHECKED_ADDRESS_RANGE_H_
+#define CRASHPAD_UTIL_NUMERIC_CHECKED_ADDRESS_RANGE_H_
+
+#include <stdint.h>
+
+#include "build/build_config.h"
+#include "util/numeric/checked_range.h"
+
+namespace crashpad {
+namespace internal {
+
+//! \brief Ensures that a range, composed of a base and a size, does not
+//! overflow the pointer type of the process it describes a range in.
+//!
+//! This class checks bases of type `ValueType` and sizes of type `SizeType`
+//! against a process whose pointer type is either 32 or 64 bits wide.
+//!
+//! Aside from varying the overall range on the basis of a process’ pointer type
+//! width, this class functions very similarly to CheckedRange.
+//!
+//! \sa CheckedMachAddressRange
+template <class ValueType, class SizeType>
+class CheckedAddressRangeGeneric {
+ public:
+ //! \brief Initializes a default range.
+ //!
+ //! The default range has base 0, size 0, and appears to be from a 32-bit
+ //! process.
+ CheckedAddressRangeGeneric();
+
+ //! \brief Initializes a range.
+ //!
+ //! See SetRange().
+ CheckedAddressRangeGeneric(bool is_64_bit, ValueType base, SizeType size);
+
+ //! \brief Sets a range’s fields.
+ //!
+ //! \param[in] is_64_bit `true` if \a base and \a size refer to addresses in a
+ //! 64-bit process; `false` if they refer to addresses in a 32-bit
+ //! process.
+ //! \param[in] base The range’s base address.
+ //! \param[in] size The range’s size.
+ void SetRange(bool is_64_bit, ValueType base, SizeType size);
+
+ //! \brief The range’s base address.
+ ValueType Base() const;
+
+ //! \brief The range’s size.
+ SizeType Size() const;
+
+ //! \brief The range’s end address (its base address plus its size).
+ ValueType End() const;
+
+ //! \brief Returns the validity of the address range.
+ //!
+ //! \return `true` if the address range is valid, `false` otherwise.
+ //!
+ //! An address range is valid if its size can be converted to the address
+ //! range’s data type without data loss, and if its end (base plus size) can
+ //! be computed without overflowing its data type.
+ bool IsValid() const;
+
+ //! \brief Returns whether this range refers to a 64-bit process.
+ bool Is64Bit() const { return is_64_bit_; }
+
+ //! \brief Returns whether the address range contains another address.
+ //!
+ //! \param[in] value The (possibly) contained address.
+ //!
+ //! \return `true` if the address range contains \a value, `false` otherwise.
+ //!
+ //! An address range contains a value if the value is greater than or equal to
+ //! its base address, and less than its end address (base address plus size).
+ //!
+ //! This method must only be called if IsValid() would return `true`.
+ bool ContainsValue(const ValueType value) const;
+
+ //! \brief Returns whether the address range contains another address range.
+ //!
+ //! \param[in] that The (possibly) contained address range.
+ //!
+ //! \return `true` if `this` address range, the containing address range,
+ //! contains \a that, the contained address range. `false` otherwise.
+ //!
+ //! An address range contains another address range when the contained address
+ //! range’s base is greater than or equal to the containing address range’s
+ //! base, and the contained address range’s end is less than or equal to the
+ //! containing address range’s end.
+ //!
+ //! This method should only be called on two CheckedAddressRangeGeneric
+ //! objects representing address ranges in the same process.
+ //!
+ //! This method must only be called if IsValid() would return `true` for both
+ //! CheckedAddressRangeGeneric objects involved.
+ bool ContainsRange(const CheckedAddressRangeGeneric& that) const;
+
+ private:
+#if defined(COMPILER_MSVC)
+ // MSVC cannot handle a union containing CheckedRange (with constructor, etc.)
+ // currently.
+ CheckedRange<uint32_t> range_32_;
+ CheckedRange<uint64_t> range_64_;
+#else
+ // The field of the union that is expressed is determined by is_64_bit_.
+ union {
+ CheckedRange<uint32_t> range_32_;
+ CheckedRange<uint64_t> range_64_;
+ };
+#endif
+
+ // Determines which field of the union is expressed.
+ bool is_64_bit_;
+
+ // Whether the base and size were valid for their data type when set. This is
+ // always true when is_64_bit_ is true because the underlying data types are
+ // 64 bits wide and there is no possibility for range and size to overflow.
+ // When is_64_bit_ is false, range_ok_ will be false if SetRange() was passed
+ // a base or size that overflowed the underlying 32-bit data type. This field
+ // is necessary because the interface exposes the address and size types
+ // uniformly, but these types are too wide for the underlying pointer and size
+ // types in 32-bit processes.
+ bool range_ok_;
+
+ DISALLOW_COPY_AND_ASSIGN(CheckedAddressRangeGeneric);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_NUMERIC_CHECKED_ADDRESS_RANGE_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/numeric/checked_address_range_test.cc b/chromium/third_party/crashpad/crashpad/util/numeric/checked_address_range_test.cc
new file mode 100644
index 00000000000..d902387ca93
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/numeric/checked_address_range_test.cc
@@ -0,0 +1,260 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/numeric/checked_address_range.h"
+
+#include <limits>
+
+#include "base/basictypes.h"
+#include "base/format_macros.h"
+#include "base/strings/stringprintf.h"
+#include "build/build_config.h"
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+using CheckedAddressRange =
+ internal::CheckedAddressRangeGeneric<uint64_t, uint64_t>;
+
+enum Validity {
+ kInvalid = false,
+ kValid,
+ kValid64Invalid32,
+};
+
+bool ExpectationForValidity32(Validity validity) {
+ return validity == kValid;
+}
+
+bool ExpectationForValidity64(Validity validity) {
+ return validity == kValid || validity == kValid64Invalid32;
+}
+
+TEST(CheckedAddressRange, IsValid) {
+ const struct TestData {
+ uint64_t base;
+ uint64_t size;
+ Validity validity;
+ } kTestData[] = {
+ {0, 0, kValid},
+ {0, 1, kValid},
+ {0, 2, kValid},
+ {0, 0x7fffffff, kValid},
+ {0, 0x80000000, kValid},
+ {0, 0xfffffffe, kValid},
+ {0, 0xffffffff, kValid},
+ {0, 0xffffffffffffffff, kValid64Invalid32},
+ {1, 0, kValid},
+ {1, 1, kValid},
+ {1, 2, kValid},
+ {1, 0x7fffffff, kValid},
+ {1, 0x80000000, kValid},
+ {1, 0xfffffffe, kValid},
+ {1, 0xffffffff, kValid64Invalid32},
+ {1, 0xfffffffffffffffe, kValid64Invalid32},
+ {1, 0xffffffffffffffff, kInvalid},
+ {0x7fffffff, 0, kValid},
+ {0x7fffffff, 1, kValid},
+ {0x7fffffff, 2, kValid},
+ {0x7fffffff, 0x7fffffff, kValid},
+ {0x7fffffff, 0x80000000, kValid},
+ {0x7fffffff, 0xfffffffe, kValid64Invalid32},
+ {0x7fffffff, 0xffffffff, kValid64Invalid32},
+ {0x80000000, 0, kValid},
+ {0x80000000, 1, kValid},
+ {0x80000000, 2, kValid},
+ {0x80000000, 0x7fffffff, kValid},
+ {0x80000000, 0x80000000, kValid64Invalid32},
+ {0x80000000, 0xfffffffe, kValid64Invalid32},
+ {0x80000000, 0xffffffff, kValid64Invalid32},
+ {0xfffffffe, 0, kValid},
+ {0xfffffffe, 1, kValid},
+ {0xfffffffe, 2, kValid64Invalid32},
+ {0xfffffffe, 0x7fffffff, kValid64Invalid32},
+ {0xfffffffe, 0x80000000, kValid64Invalid32},
+ {0xfffffffe, 0xfffffffe, kValid64Invalid32},
+ {0xfffffffe, 0xffffffff, kValid64Invalid32},
+ {0xffffffff, 0, kValid},
+ {0xffffffff, 1, kValid64Invalid32},
+ {0xffffffff, 2, kValid64Invalid32},
+ {0xffffffff, 0x7fffffff, kValid64Invalid32},
+ {0xffffffff, 0x80000000, kValid64Invalid32},
+ {0xffffffff, 0xfffffffe, kValid64Invalid32},
+ {0xffffffff, 0xffffffff, kValid64Invalid32},
+ {0x7fffffffffffffff, 0, kValid64Invalid32},
+ {0x7fffffffffffffff, 1, kValid64Invalid32},
+ {0x7fffffffffffffff, 2, kValid64Invalid32},
+ {0x7fffffffffffffff, 0x7fffffffffffffff, kValid64Invalid32},
+ {0x7fffffffffffffff, 0x8000000000000000, kValid64Invalid32},
+ {0x7fffffffffffffff, 0x8000000000000001, kInvalid},
+ {0x7fffffffffffffff, 0xfffffffffffffffe, kInvalid},
+ {0x7fffffffffffffff, 0xffffffffffffffff, kInvalid},
+ {0x8000000000000000, 0, kValid64Invalid32},
+ {0x8000000000000000, 1, kValid64Invalid32},
+ {0x8000000000000000, 2, kValid64Invalid32},
+ {0x8000000000000000, 0x7fffffffffffffff, kValid64Invalid32},
+ {0x8000000000000000, 0x8000000000000000, kInvalid},
+ {0x8000000000000000, 0x8000000000000001, kInvalid},
+ {0x8000000000000000, 0xfffffffffffffffe, kInvalid},
+ {0x8000000000000000, 0xffffffffffffffff, kInvalid},
+ {0xfffffffffffffffe, 0, kValid64Invalid32},
+ {0xfffffffffffffffe, 1, kValid64Invalid32},
+ {0xfffffffffffffffe, 2, kInvalid},
+ {0xffffffffffffffff, 0, kValid64Invalid32},
+ {0xffffffffffffffff, 1, kInvalid},
+ };
+
+ for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ const TestData& testcase = kTestData[index];
+ SCOPED_TRACE(base::StringPrintf("index %" PRIuS
+ ", base 0x%llx, size 0x%llx",
+ index,
+ testcase.base,
+ testcase.size));
+
+ CheckedAddressRange range_32(false, testcase.base, testcase.size);
+ EXPECT_EQ(ExpectationForValidity32(testcase.validity), range_32.IsValid());
+
+ CheckedAddressRange range_64(true, testcase.base, testcase.size);
+ EXPECT_EQ(ExpectationForValidity64(testcase.validity), range_64.IsValid());
+ }
+}
+
+TEST(CheckedAddressRange, ContainsValue) {
+ const struct TestData {
+ uint64_t value;
+ bool expectation;
+ } kTestData[] = {
+ {0, false},
+ {1, false},
+ {0x1fff, false},
+ {0x2000, true},
+ {0x2001, true},
+ {0x2ffe, true},
+ {0x2fff, true},
+ {0x3000, false},
+ {0x3001, false},
+ {0x7fffffff, false},
+ {0x80000000, false},
+ {0x80000001, false},
+ {0x80001fff, false},
+ {0x80002000, false},
+ {0x80002001, false},
+ {0x80002ffe, false},
+ {0x80002fff, false},
+ {0x80003000, false},
+ {0x80003001, false},
+ {0xffffcfff, false},
+ {0xffffdfff, false},
+ {0xffffefff, false},
+ {0xffffffff, false},
+ {0x100000000, false},
+ {0xffffffffffffffff, false},
+ };
+
+ CheckedAddressRange parent_range_32(false, 0x2000, 0x1000);
+ ASSERT_TRUE(parent_range_32.IsValid());
+
+ for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ const TestData& testcase = kTestData[index];
+ SCOPED_TRACE(base::StringPrintf(
+ "index %" PRIuS ", value 0x%llx", index, testcase.value));
+
+ EXPECT_EQ(testcase.expectation,
+ parent_range_32.ContainsValue(testcase.value));
+ }
+
+ CheckedAddressRange parent_range_64(true, 0x100000000, 0x1000);
+ ASSERT_TRUE(parent_range_64.IsValid());
+ EXPECT_FALSE(parent_range_64.ContainsValue(0xffffffff));
+ EXPECT_TRUE(parent_range_64.ContainsValue(0x100000000));
+ EXPECT_TRUE(parent_range_64.ContainsValue(0x100000001));
+ EXPECT_TRUE(parent_range_64.ContainsValue(0x100000fff));
+ EXPECT_FALSE(parent_range_64.ContainsValue(0x100001000));
+}
+
+TEST(CheckedAddressRange, ContainsRange) {
+ const struct TestData {
+ uint64_t base;
+ uint64_t size;
+ bool expectation;
+ } kTestData[] = {
+ {0, 0, false},
+ {0, 1, false},
+ {0x2000, 0x1000, true},
+ {0, 0x2000, false},
+ {0x3000, 0x1000, false},
+ {0x1800, 0x1000, false},
+ {0x2800, 0x1000, false},
+ {0x2000, 0x800, true},
+ {0x2800, 0x800, true},
+ {0x2400, 0x800, true},
+ {0x2800, 0, true},
+ {0x2000, 0xffffdfff, false},
+ {0x2800, 0xffffd7ff, false},
+ {0x3000, 0xffffcfff, false},
+ {0xfffffffe, 1, false},
+ {0xffffffff, 0, false},
+ {0x1fff, 0, false},
+ {0x2000, 0, true},
+ {0x2001, 0, true},
+ {0x2fff, 0, true},
+ {0x3000, 0, true},
+ {0x3001, 0, false},
+ {0x1fff, 1, false},
+ {0x2000, 1, true},
+ {0x2001, 1, true},
+ {0x2fff, 1, true},
+ {0x3000, 1, false},
+ {0x3001, 1, false},
+ };
+
+ CheckedAddressRange parent_range_32(false, 0x2000, 0x1000);
+ ASSERT_TRUE(parent_range_32.IsValid());
+
+ for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ const TestData& testcase = kTestData[index];
+ SCOPED_TRACE(base::StringPrintf("index %" PRIuS
+ ", base 0x%llx, size 0x%llx",
+ index,
+ testcase.base,
+ testcase.size));
+
+ CheckedAddressRange child_range_32(false, testcase.base, testcase.size);
+ ASSERT_TRUE(child_range_32.IsValid());
+ EXPECT_EQ(testcase.expectation,
+ parent_range_32.ContainsRange(child_range_32));
+ }
+
+ CheckedAddressRange parent_range_64(true, 0x100000000, 0x1000);
+ ASSERT_TRUE(parent_range_64.IsValid());
+
+ CheckedAddressRange child_range_64(true, 0xffffffff, 2);
+ EXPECT_FALSE(parent_range_64.ContainsRange(child_range_64));
+
+ child_range_64.SetRange(true, 0x100000000, 2);
+ EXPECT_TRUE(parent_range_64.ContainsRange(child_range_64));
+
+ child_range_64.SetRange(true, 0x100000ffe, 2);
+ EXPECT_TRUE(parent_range_64.ContainsRange(child_range_64));
+
+ child_range_64.SetRange(true, 0x100000fff, 2);
+ EXPECT_FALSE(parent_range_64.ContainsRange(child_range_64));
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/numeric/checked_range.h b/chromium/third_party/crashpad/crashpad/util/numeric/checked_range.h
new file mode 100644
index 00000000000..982ee4f2af7
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/numeric/checked_range.h
@@ -0,0 +1,115 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_NUMERIC_CHECKED_RANGE_H_
+#define CRASHPAD_UTIL_NUMERIC_CHECKED_RANGE_H_
+
+#include <limits>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/numerics/safe_math.h"
+
+namespace crashpad {
+
+//! \brief Ensures that a range, composed of a base and size, does not overflow
+//! its data type.
+template <typename ValueType, typename SizeType = ValueType>
+class CheckedRange {
+ public:
+ CheckedRange(ValueType base, SizeType size) {
+ static_assert(!std::numeric_limits<SizeType>::is_signed,
+ "SizeType must be unsigned");
+ SetRange(base, size);
+ }
+
+ //! \brief Sets the range’s base and size to \a base and \a size,
+ //! respectively.
+ void SetRange(ValueType base, SizeType size) {
+ base_ = base;
+ size_ = size;
+ }
+
+ //! \brief The range’s base.
+ ValueType base() const { return base_; }
+
+ //! \brief The range’s size.
+ SizeType size() const { return size_; }
+
+ //! \brief The range’s end (its base plus its size).
+ ValueType end() const { return base_ + size_; }
+
+ //! \brief Returns the validity of the range.
+ //!
+ //! \return `true` if the range is valid, `false` otherwise.
+ //!
+ //! A range is valid if its size can be converted to the range’s data type
+ //! without data loss, and if its end (base plus size) can be computed without
+ //! overflowing its data type.
+ bool IsValid() const {
+ if (!base::IsValueInRangeForNumericType<ValueType, SizeType>(size_)) {
+ return false;
+ }
+ base::CheckedNumeric<ValueType> checked_end(base_);
+ checked_end += implicit_cast<ValueType>(size_);
+ return checked_end.IsValid();
+ }
+
+ //! \brief Returns whether the range contains another value.
+ //!
+ //! \param[in] value The (possibly) contained value.
+ //!
+ //! \return `true` if the range contains \a value, `false` otherwise.
+ //!
+ //! A range contains a value if the value is greater than or equal to its
+ //! base, and less than its end (base plus size).
+ //!
+ //! This method must only be called if IsValid() would return `true`.
+ bool ContainsValue(ValueType value) const {
+ DCHECK(IsValid());
+
+ return value >= base() && value < end();
+ }
+
+ //! \brief Returns whether the range contains another range.
+ //!
+ //! \param[in] that The (possibly) contained range.
+ //!
+ //! \return `true` if `this` range, the containing range, contains \a that,
+ //! the contained range. `false` otherwise.
+ //!
+ //! A range contains another range when the contained range’s base is greater
+ //! than or equal to the containing range’s base, and the contained range’s
+ //! end is less than or equal to the containing range’s end.
+ //!
+ //! This method must only be called if IsValid() would return `true` for both
+ //! CheckedRange objects involved.
+ bool ContainsRange(const CheckedRange<ValueType, SizeType>& that) const {
+ DCHECK(IsValid());
+ DCHECK(that.IsValid());
+
+ return that.base() >= base() && that.end() <= end();
+ }
+
+ private:
+ ValueType base_;
+ SizeType size_;
+
+ DISALLOW_COPY_AND_ASSIGN(CheckedRange);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_NUMERIC_CHECKED_RANGE_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/numeric/checked_range_test.cc b/chromium/third_party/crashpad/crashpad/util/numeric/checked_range_test.cc
new file mode 100644
index 00000000000..9c72526a6a8
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/numeric/checked_range_test.cc
@@ -0,0 +1,251 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/numeric/checked_range.h"
+
+#include <stdint.h>
+
+#include <limits>
+
+#include "base/basictypes.h"
+#include "base/format_macros.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(CheckedRange, IsValid) {
+ const struct UnsignedTestData {
+ uint32_t base;
+ uint32_t size;
+ bool valid;
+ } kUnsignedTestData[] = {
+ {0, 0, true},
+ {0, 1, true},
+ {0, 2, true},
+ {0, 0x7fffffff, true},
+ {0, 0x80000000, true},
+ {0, 0xfffffffe, true},
+ {0, 0xffffffff, true},
+ {1, 0, true},
+ {1, 1, true},
+ {1, 2, true},
+ {1, 0x7fffffff, true},
+ {1, 0x80000000, true},
+ {1, 0xfffffffe, true},
+ {1, 0xffffffff, false},
+ {0x7fffffff, 0, true},
+ {0x7fffffff, 1, true},
+ {0x7fffffff, 2, true},
+ {0x7fffffff, 0x7fffffff, true},
+ {0x7fffffff, 0x80000000, true},
+ {0x7fffffff, 0xfffffffe, false},
+ {0x7fffffff, 0xffffffff, false},
+ {0x80000000, 0, true},
+ {0x80000000, 1, true},
+ {0x80000000, 2, true},
+ {0x80000000, 0x7fffffff, true},
+ {0x80000000, 0x80000000, false},
+ {0x80000000, 0xfffffffe, false},
+ {0x80000000, 0xffffffff, false},
+ {0xfffffffe, 0, true},
+ {0xfffffffe, 1, true},
+ {0xfffffffe, 2, false},
+ {0xfffffffe, 0x7fffffff, false},
+ {0xfffffffe, 0x80000000, false},
+ {0xfffffffe, 0xfffffffe, false},
+ {0xfffffffe, 0xffffffff, false},
+ {0xffffffff, 0, true},
+ {0xffffffff, 1, false},
+ {0xffffffff, 2, false},
+ {0xffffffff, 0x7fffffff, false},
+ {0xffffffff, 0x80000000, false},
+ {0xffffffff, 0xfffffffe, false},
+ {0xffffffff, 0xffffffff, false},
+ };
+
+ for (size_t index = 0; index < arraysize(kUnsignedTestData); ++index) {
+ const UnsignedTestData& testcase = kUnsignedTestData[index];
+ SCOPED_TRACE(base::StringPrintf("unsigned index %" PRIuS
+ ", base 0x%x, size 0x%x",
+ index,
+ testcase.base,
+ testcase.size));
+
+ CheckedRange<uint32_t> range(testcase.base, testcase.size);
+ EXPECT_EQ(testcase.valid, range.IsValid());
+ }
+
+ const int32_t kMinInt32 = std::numeric_limits<int32_t>::min();
+ const struct SignedTestData {
+ int32_t base;
+ uint32_t size;
+ bool valid;
+ } kSignedTestData[] = {
+ {0, 0, true},
+ {0, 1, true},
+ {0, 2, true},
+ {0, 0x7fffffff, true},
+ {0, 0x80000000, false},
+ {0, 0xfffffffe, false},
+ {0, 0xffffffff, false},
+ {1, 0, true},
+ {1, 1, true},
+ {1, 2, true},
+ {1, 0x7fffffff, false},
+ {1, 0x80000000, false},
+ {1, 0xfffffffe, false},
+ {1, 0xffffffff, false},
+ {0x7fffffff, 0, true},
+ {0x7fffffff, 1, false},
+ {0x7fffffff, 2, false},
+ {0x7fffffff, 0x7fffffff, false},
+ {0x7fffffff, 0x80000000, false},
+ {0x7fffffff, 0xfffffffe, false},
+ {0x7fffffff, 0xffffffff, false},
+ {kMinInt32, 0, true},
+ {kMinInt32, 1, true},
+ {kMinInt32, 2, true},
+ {kMinInt32, 0x7fffffff, true},
+ {kMinInt32, 0x80000000, false},
+ {kMinInt32, 0xfffffffe, false},
+ {kMinInt32, 0xffffffff, false},
+ {-2, 0, true},
+ {-2, 1, true},
+ {-2, 2, true},
+ {-2, 0x7fffffff, true},
+ {-2, 0x80000000, false},
+ {-2, 0xfffffffe, false},
+ {-2, 0xffffffff, false},
+ {-1, 0, true},
+ {-1, 1, true},
+ {-1, 2, true},
+ {-1, 0x7fffffff, true},
+ {-1, 0x80000000, false},
+ {-1, 0xfffffffe, false},
+ {-1, 0xffffffff, false},
+ };
+
+ for (size_t index = 0; index < arraysize(kSignedTestData); ++index) {
+ const SignedTestData& testcase = kSignedTestData[index];
+ SCOPED_TRACE(base::StringPrintf("signed index %" PRIuS
+ ", base 0x%x, size 0x%x",
+ index,
+ testcase.base,
+ testcase.size));
+
+ CheckedRange<int32_t, uint32_t> range(testcase.base, testcase.size);
+ EXPECT_EQ(testcase.valid, range.IsValid());
+ }
+}
+
+TEST(CheckedRange, ContainsValue) {
+ const struct TestData {
+ uint32_t value;
+ bool valid;
+ } kTestData[] = {
+ {0, false},
+ {1, false},
+ {0x1fff, false},
+ {0x2000, true},
+ {0x2001, true},
+ {0x2ffe, true},
+ {0x2fff, true},
+ {0x3000, false},
+ {0x3001, false},
+ {0x7fffffff, false},
+ {0x80000000, false},
+ {0x80000001, false},
+ {0x80001fff, false},
+ {0x80002000, false},
+ {0x80002001, false},
+ {0x80002ffe, false},
+ {0x80002fff, false},
+ {0x80003000, false},
+ {0x80003001, false},
+ {0xffffcfff, false},
+ {0xffffdfff, false},
+ {0xffffefff, false},
+ {0xffffffff, false},
+ };
+
+ CheckedRange<uint32_t> parent_range(0x2000, 0x1000);
+ ASSERT_TRUE(parent_range.IsValid());
+
+ for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ const TestData& testcase = kTestData[index];
+ SCOPED_TRACE(base::StringPrintf(
+ "index %" PRIuS ", value 0x%x", index, testcase.value));
+
+ EXPECT_EQ(testcase.valid, parent_range.ContainsValue(testcase.value));
+ }
+}
+
+TEST(CheckedRange, ContainsRange) {
+ const struct TestData {
+ uint32_t base;
+ uint32_t size;
+ bool valid;
+ } kTestData[] = {
+ {0, 0, false},
+ {0, 1, false},
+ {0x2000, 0x1000, true},
+ {0, 0x2000, false},
+ {0x3000, 0x1000, false},
+ {0x1800, 0x1000, false},
+ {0x2800, 0x1000, false},
+ {0x2000, 0x800, true},
+ {0x2800, 0x800, true},
+ {0x2400, 0x800, true},
+ {0x2800, 0, true},
+ {0x2000, 0xffffdfff, false},
+ {0x2800, 0xffffd7ff, false},
+ {0x3000, 0xffffcfff, false},
+ {0xfffffffe, 1, false},
+ {0xffffffff, 0, false},
+ {0x1fff, 0, false},
+ {0x2000, 0, true},
+ {0x2001, 0, true},
+ {0x2fff, 0, true},
+ {0x3000, 0, true},
+ {0x3001, 0, false},
+ {0x1fff, 1, false},
+ {0x2000, 1, true},
+ {0x2001, 1, true},
+ {0x2fff, 1, true},
+ {0x3000, 1, false},
+ {0x3001, 1, false},
+ };
+
+ CheckedRange<uint32_t> parent_range(0x2000, 0x1000);
+ ASSERT_TRUE(parent_range.IsValid());
+
+ for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ const TestData& testcase = kTestData[index];
+ SCOPED_TRACE(base::StringPrintf("index %" PRIuS ", base 0x%x, size 0x%x",
+ index,
+ testcase.base,
+ testcase.size));
+
+ CheckedRange<uint32_t> child_range(testcase.base, testcase.size);
+ ASSERT_TRUE(child_range.IsValid());
+ EXPECT_EQ(testcase.valid, parent_range.ContainsRange(child_range));
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/numeric/in_range_cast.h b/chromium/third_party/crashpad/crashpad/util/numeric/in_range_cast.h
new file mode 100644
index 00000000000..3dba10afa16
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/numeric/in_range_cast.h
@@ -0,0 +1,44 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_NUMERIC_IN_RANGE_CAST_H_
+#define CRASHPAD_UTIL_NUMERIC_IN_RANGE_CAST_H_
+
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+
+namespace crashpad {
+
+//! \brief Casts to a different type if it can be done without data loss,
+//! logging a warning message and returing a default value otherwise.
+//!
+//! \param[in] source The value to convert and return.
+//! \param[in] default_value The default value to return, in the event that \a
+//! source cannot be represented in the destination type.
+//!
+//! \return \a source if it can be represented in the destination type,
+//! otherwise \a default_value.
+template <typename Destination, typename Source>
+Destination InRangeCast(Source source, Destination default_value) {
+ if (base::IsValueInRangeForNumericType<Destination>(source)) {
+ return static_cast<Destination>(source);
+ }
+
+ LOG(WARNING) << "value " << source << " out of range";
+ return static_cast<Destination>(default_value);
+}
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_NUMERIC_IN_RANGE_CAST_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/numeric/in_range_cast_test.cc b/chromium/third_party/crashpad/crashpad/util/numeric/in_range_cast_test.cc
new file mode 100644
index 00000000000..5ed9b17295a
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/numeric/in_range_cast_test.cc
@@ -0,0 +1,119 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/numeric/in_range_cast.h"
+
+#include <stdint.h>
+
+#include <limits>
+
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+const int32_t kInt32Min = std::numeric_limits<int32_t>::min();
+const int64_t kInt64Min = std::numeric_limits<int64_t>::min();
+
+TEST(InRangeCast, Uint32) {
+ EXPECT_EQ(0u, InRangeCast<uint32_t>(0, 1));
+ EXPECT_EQ(1u, InRangeCast<uint32_t>(1, 1));
+ EXPECT_EQ(2u, InRangeCast<uint32_t>(2, 1));
+ EXPECT_EQ(0u, InRangeCast<uint32_t>(-1, 0));
+ EXPECT_EQ(1u, InRangeCast<uint32_t>(-1, 1));
+ EXPECT_EQ(2u, InRangeCast<uint32_t>(-1, 2));
+ EXPECT_EQ(0xffffffffu, InRangeCast<uint32_t>(0xffffffffu, 1));
+ EXPECT_EQ(0xffffffffu, InRangeCast<uint32_t>(UINT64_C(0xffffffff), 1));
+ EXPECT_EQ(1u, InRangeCast<uint32_t>(UINT64_C(0x100000000), 1));
+ EXPECT_EQ(1u, InRangeCast<uint32_t>(UINT64_C(0x100000001), 1));
+ EXPECT_EQ(1u, InRangeCast<uint32_t>(kInt32Min, 1));
+ EXPECT_EQ(0xffffffffu, InRangeCast<uint32_t>(-1, 0xffffffffu));
+}
+
+TEST(InRangeCast, Int32) {
+ EXPECT_EQ(0, InRangeCast<int32_t>(0, 1));
+ EXPECT_EQ(1, InRangeCast<int32_t>(1, 1));
+ EXPECT_EQ(2, InRangeCast<int32_t>(2, 1));
+ EXPECT_EQ(-1, InRangeCast<int32_t>(-1, 1));
+ EXPECT_EQ(0x7fffffff, InRangeCast<int32_t>(0x7fffffff, 1));
+ EXPECT_EQ(0x7fffffff, InRangeCast<int32_t>(0x7fffffffu, 1));
+ EXPECT_EQ(1, InRangeCast<int32_t>(0x80000000u, 1));
+ EXPECT_EQ(1, InRangeCast<int32_t>(0xffffffffu, 1));
+ EXPECT_EQ(1, InRangeCast<int32_t>(INT64_C(0x80000000), 1));
+ EXPECT_EQ(1, InRangeCast<int32_t>(INT64_C(0xffffffff), 1));
+ EXPECT_EQ(1, InRangeCast<int32_t>(INT64_C(0x100000000), 1));
+ EXPECT_EQ(kInt32Min, InRangeCast<int32_t>(kInt32Min, 1));
+ EXPECT_EQ(kInt32Min,
+ InRangeCast<int32_t>(implicit_cast<int64_t>(kInt32Min), 1));
+ EXPECT_EQ(1, InRangeCast<int32_t>(implicit_cast<int64_t>(kInt32Min) - 1, 1));
+ EXPECT_EQ(0, InRangeCast<int32_t>(0xffffffffu, 0));
+ EXPECT_EQ(-1, InRangeCast<int32_t>(0xffffffffu, -1));
+ EXPECT_EQ(kInt32Min, InRangeCast<int32_t>(0xffffffffu, kInt32Min));
+ EXPECT_EQ(0x7fffffff, InRangeCast<int32_t>(0xffffffffu, 0x7fffffff));
+}
+
+TEST(InRangeCast, Uint64) {
+ EXPECT_EQ(0u, InRangeCast<uint64_t>(0, 1));
+ EXPECT_EQ(1u, InRangeCast<uint64_t>(1, 1));
+ EXPECT_EQ(2u, InRangeCast<uint64_t>(2, 1));
+ EXPECT_EQ(0u, InRangeCast<uint64_t>(-1, 0));
+ EXPECT_EQ(1u, InRangeCast<uint64_t>(-1, 1));
+ EXPECT_EQ(2u, InRangeCast<uint64_t>(-1, 2));
+ EXPECT_EQ(0xffffffffu, InRangeCast<uint64_t>(0xffffffffu, 1));
+ EXPECT_EQ(0xffffffffu, InRangeCast<uint64_t>(UINT64_C(0xffffffff), 1));
+ EXPECT_EQ(UINT64_C(0x100000000),
+ InRangeCast<uint64_t>(UINT64_C(0x100000000), 1));
+ EXPECT_EQ(UINT64_C(0x100000001),
+ InRangeCast<uint64_t>(UINT64_C(0x100000001), 1));
+ EXPECT_EQ(1u, InRangeCast<uint64_t>(kInt32Min, 1));
+ EXPECT_EQ(1u, InRangeCast<uint64_t>(INT64_C(-1), 1));
+ EXPECT_EQ(1u, InRangeCast<uint64_t>(kInt32Min, 1));
+ EXPECT_EQ(1u, InRangeCast<uint64_t>(kInt64Min, 1));
+ EXPECT_EQ(UINT64_C(0xffffffffffffffff),
+ InRangeCast<uint64_t>(-1, UINT64_C(0xffffffffffffffff)));
+}
+
+TEST(InRangeCast, Int64) {
+ EXPECT_EQ(0, InRangeCast<int64_t>(0, 1));
+ EXPECT_EQ(1, InRangeCast<int64_t>(1, 1));
+ EXPECT_EQ(2, InRangeCast<int64_t>(2, 1));
+ EXPECT_EQ(-1, InRangeCast<int64_t>(-1, 1));
+ EXPECT_EQ(0x7fffffff, InRangeCast<int64_t>(0x7fffffff, 1));
+ EXPECT_EQ(0x7fffffff, InRangeCast<int64_t>(0x7fffffffu, 1));
+ EXPECT_EQ(INT64_C(0x80000000), InRangeCast<int64_t>(0x80000000u, 1));
+ EXPECT_EQ(INT64_C(0xffffffff), InRangeCast<int64_t>(0xffffffffu, 1));
+ EXPECT_EQ(INT64_C(0x80000000), InRangeCast<int64_t>(INT64_C(0x80000000), 1));
+ EXPECT_EQ(INT64_C(0xffffffff), InRangeCast<int64_t>(INT64_C(0xffffffff), 1));
+ EXPECT_EQ(INT64_C(0x100000000),
+ InRangeCast<int64_t>(INT64_C(0x100000000), 1));
+ EXPECT_EQ(INT64_C(0x7fffffffffffffff),
+ InRangeCast<int64_t>(INT64_C(0x7fffffffffffffff), 1));
+ EXPECT_EQ(1, InRangeCast<int64_t>(UINT64_C(0x8000000000000000), 1));
+ EXPECT_EQ(1, InRangeCast<int64_t>(UINT64_C(0xffffffffffffffff), 1));
+ EXPECT_EQ(kInt32Min, InRangeCast<int64_t>(kInt32Min, 1));
+ EXPECT_EQ(kInt32Min,
+ InRangeCast<int64_t>(implicit_cast<int64_t>(kInt32Min), 1));
+ EXPECT_EQ(0, InRangeCast<int64_t>(UINT64_C(0xffffffffffffffff), 0));
+ EXPECT_EQ(-1, InRangeCast<int64_t>(UINT64_C(0xffffffffffffffff), -1));
+ EXPECT_EQ(kInt64Min,
+ InRangeCast<int64_t>(UINT64_C(0xffffffffffffffff), kInt64Min));
+ EXPECT_EQ(INT64_C(0x7fffffffffffffff),
+ InRangeCast<int64_t>(UINT64_C(0xffffffffffffffff),
+ INT64_C(0x7fffffffffffffff)));
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/numeric/int128.h b/chromium/third_party/crashpad/crashpad/util/numeric/int128.h
new file mode 100644
index 00000000000..c57bed6d282
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/numeric/int128.h
@@ -0,0 +1,52 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_NUMERIC_INT128_H_
+#define CRASHPAD_UTIL_NUMERIC_INT128_H_
+
+#include <stdint.h>
+
+#include "build/build_config.h"
+
+namespace crashpad {
+
+//! \brief Stores a 128-bit quantity.
+//!
+//! This structure is organized so that 128-bit quantities are laid out in
+//! memory according to the system’s natural byte order. If a system offers a
+//! native 128-bit type, it should be possible to bit_cast<> between that type
+//! and this one.
+//!
+//! This structure is designed to have the same layout, although not the same
+//! field names, as the Windows SDK’s `M128A` type from `<winnt.h>`. It is
+//! provided here instead of in `compat` because it is useful outside of the
+//! scope of data structures defined by the Windows SDK.
+struct uint128_struct {
+#if defined(ARCH_CPU_LITTLE_ENDIAN) || DOXYGEN
+ //! \brief The low 64 bits of the 128-bit quantity.
+ uint64_t lo;
+
+ //! \brief The high 64 bits of the 128-bit quantity.
+ uint64_t hi;
+#else
+ uint64_t hi;
+ uint64_t lo;
+#endif
+};
+
+static_assert(sizeof(uint128_struct) == 16, "uint128 must be 16 bytes");
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_NUMERIC_INT128_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/numeric/int128_test.cc b/chromium/third_party/crashpad/crashpad/util/numeric/int128_test.cc
new file mode 100644
index 00000000000..e2f02a4349d
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/numeric/int128_test.cc
@@ -0,0 +1,44 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/numeric/int128.h"
+
+#include "base/basictypes.h"
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(Int128, UInt128) {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ const uint8_t kBytes[] =
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+#else
+ const uint8_t kBytes[] =
+ {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
+#endif
+
+ uint128_struct uint128;
+ static_assert(sizeof(uint128) == sizeof(kBytes), "sizes must be equal");
+
+ uint128 = bit_cast<uint128_struct>(kBytes);
+
+ EXPECT_EQ(0x0706050403020100u, uint128.lo);
+ EXPECT_EQ(0x0f0e0d0c0b0a0908u, uint128.hi);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/numeric/safe_assignment.h b/chromium/third_party/crashpad/crashpad/util/numeric/safe_assignment.h
new file mode 100644
index 00000000000..358d9837015
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/numeric/safe_assignment.h
@@ -0,0 +1,44 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_NUMERIC_SAFE_ASSIGNMENT_H_
+#define CRASHPAD_UTIL_NUMERIC_SAFE_ASSIGNMENT_H_
+
+#include "base/numerics/safe_conversions.h"
+
+namespace crashpad {
+
+//! \brief Performs an assignment if it can be done safely, and signals if it
+//! cannot be done safely.
+//!
+//! \param[out] destination A pointer to the variable to be assigned to.
+//! \param[in] source The value to assign.
+//!
+//! \return `true` if \a source is in the range supported by the type of \a
+//! *destination, with the assignment to \a *destination having been
+//! performed. `false` if the assignment cannot be completed safely because
+//! \a source is outside of this range.
+template <typename Destination, typename Source>
+bool AssignIfInRange(Destination* destination, Source source) {
+ if (!base::IsValueInRangeForNumericType<Destination>(source)) {
+ return false;
+ }
+
+ *destination = static_cast<Destination>(source);
+ return true;
+}
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_NUMERIC_SAFE_ASSIGNMENT_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/posix/close_multiple.cc b/chromium/third_party/crashpad/crashpad/util/posix/close_multiple.cc
new file mode 100644
index 00000000000..a61ee1cd20e
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/posix/close_multiple.cc
@@ -0,0 +1,158 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/posix/close_multiple.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+#include "util/numeric/safe_assignment.h"
+
+// Everything in this file is expected to execute between fork() and exec(),
+// so everything called here must be acceptable in this context. However,
+// logging code that is not expected to execute under normal circumstances is
+// currently permitted.
+
+namespace crashpad {
+namespace {
+
+// This function attempts to close |fd| or mark it as close-on-exec. On systems
+// where close-on-exec is attempted, a failure to mark it close-on-exec will be
+// followed by an attempt to close it. |ebadf_ok| should be set to |true| if
+// the caller is attempting to close the file descriptor “blind,” that is,
+// without knowledge that it is or is not a valid file descriptor.
+void CloseNowOrOnExec(int fd, bool ebadf_ok) {
+ int rv;
+
+#if defined(OS_MACOSX)
+ // Try to set close-on-exec, to avoid attempting to close a guarded FD with
+ // a close guard set.
+ rv = fcntl(fd, F_SETFD, FD_CLOEXEC);
+ if (rv != -1 || (ebadf_ok && errno == EBADF)) {
+ return;
+ }
+ PLOG(WARNING) << "fcntl";
+#endif
+
+ rv = IGNORE_EINTR(close(fd));
+ if (rv != 0 && !(ebadf_ok && errno == EBADF)) {
+ PLOG(WARNING) << "close";
+ }
+}
+
+struct ScopedDIRCloser {
+ void operator()(DIR* dir) const {
+ if (dir) {
+ if (closedir(dir) < 0) {
+ PLOG(ERROR) << "closedir";
+ }
+ }
+ }
+};
+
+using ScopedDIR = scoped_ptr<DIR, ScopedDIRCloser>;
+
+// This function implements CloseMultipleNowOrOnExec() using an operating
+// system-specific FD directory to determine which file descriptors are open.
+// This is an advantage over looping over all possible file descriptors, because
+// no attempt needs to be made to close file descriptors that are not open.
+bool CloseMultipleNowOrOnExecUsingFDDir(int fd, int preserve_fd) {
+#if defined(OS_MACOSX)
+ const char kFDDir[] = "/dev/fd";
+#elif defined(OS_LINUX)
+ const char kFDDir[] = "/proc/self/fd";
+#endif
+
+ DIR* dir = opendir(kFDDir);
+ if (!dir) {
+ PLOG(WARNING) << "opendir";
+ return false;
+ }
+
+ ScopedDIR dir_owner(dir);
+
+ int dir_fd = dirfd(dir);
+ if (dir_fd == -1) {
+ PLOG(WARNING) << "dirfd";
+ return false;
+ }
+
+ dirent entry;
+ dirent* result;
+ int rv;
+ while ((rv = readdir_r(dir, &entry, &result)) == 0 && result != nullptr) {
+ const char* entry_name = &(*result->d_name);
+ if (strcmp(entry_name, ".") == 0 || strcmp(entry_name, "..") == 0) {
+ continue;
+ }
+
+ char* end;
+ long entry_fd_long = strtol(entry_name, &end, 10);
+ if (entry_name[0] == '\0' || *end) {
+ LOG(ERROR) << "unexpected entry " << entry_name;
+ return false;
+ }
+
+ int entry_fd;
+ if (!AssignIfInRange(&entry_fd, entry_fd_long)) {
+ LOG(ERROR) << "out-of-range fd " << entry_name;
+ return false;
+ }
+
+ if (entry_fd >= fd && entry_fd != preserve_fd && entry_fd != dir_fd) {
+ CloseNowOrOnExec(entry_fd, false);
+ }
+ }
+
+ return true;
+}
+
+} // namespace
+
+void CloseMultipleNowOrOnExec(int fd, int preserve_fd) {
+ if (CloseMultipleNowOrOnExecUsingFDDir(fd, preserve_fd)) {
+ return;
+ }
+
+ // Fallback: close every file descriptor starting at |fd| and ending at the
+ // system’s file descriptor limit. Check a few values and use the highest as
+ // the limit, because these may be based on the file descriptor limit set by
+ // setrlimit(), and higher-numbered file descriptors may have been opened
+ // prior to the limit being lowered. For Mac OS X, see 10.9.2
+ // Libc-997.90.3/gen/FreeBSD/sysconf.c sysconf() and 10.9.4
+ // xnu-2422.110.17/bsd/kern/kern_descrip.c getdtablesize(), which both return
+ // the current RLIMIT_NOFILE value, not the maximum possible file descriptor.
+ int max_fd = std::max(implicit_cast<int>(sysconf(_SC_OPEN_MAX)), OPEN_MAX);
+ max_fd = std::max(max_fd, getdtablesize());
+
+ for (int entry_fd = fd; entry_fd < max_fd; ++entry_fd) {
+ if (entry_fd != preserve_fd) {
+ CloseNowOrOnExec(entry_fd, true);
+ }
+ }
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/posix/close_multiple.h b/chromium/third_party/crashpad/crashpad/util/posix/close_multiple.h
new file mode 100644
index 00000000000..2a66f28a83c
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/posix/close_multiple.h
@@ -0,0 +1,44 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_POSIX_CLOSE_MULTIPLE_H_
+#define CRASHPAD_UTIL_POSIX_CLOSE_MULTIPLE_H_
+
+namespace crashpad {
+
+//! \brief Close multiple file descriptors or mark them close-on-exec.
+//!
+//! This is similar to the BSD/Solaris-style `closefrom()` routine, which closes
+//! all open file descriptors equal to or higher than its \a fd argument. This
+//! function must not be called while other threads are active. It is intended
+//! to be used in a child process created by `fork()`, prior to calling an
+//! `exec()`-family function. This guarantees that a (possibly untrustworthy)
+//! child process does not inherit file descriptors that it has no need for.
+//!
+//! Unlike the BSD function, this function may not close file descriptors
+//! immediately, but may instead mark them as close-on-exec. The actual behavior
+//! chosen is specific to the operating system. On Mac OS X, file descriptors
+//! are marked close-on-exec instead of being closed outright in order to avoid
+//! raising `EXC_GUARD` exceptions for guarded file descriptors that are
+//! protected against `close()`.
+//!
+//! \param[in] fd The lowest file descriptor to close or set as close-on-exec.
+//! \param[in] preserve_fd A file descriptor to preserve and not close (or set
+//! as close-on-exec), even if it is open and its value is greater than \a
+//! fd. To not preserve any file descriptor, pass `-1` for this parameter.
+void CloseMultipleNowOrOnExec(int fd, int preserve_fd);
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_POSIX_CLOSE_MULTIPLE_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/posix/close_stdio.cc b/chromium/third_party/crashpad/crashpad/util/posix/close_stdio.cc
new file mode 100644
index 00000000000..8ff59d0af31
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/posix/close_stdio.cc
@@ -0,0 +1,52 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/posix/close_stdio.h"
+
+#include <fcntl.h>
+#include <paths.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+namespace crashpad {
+
+namespace {
+
+void CloseStdioStream(int desired_fd, int oflag) {
+ base::ScopedFD fd(HANDLE_EINTR(open(_PATH_DEVNULL, oflag)));
+ if (fd == desired_fd) {
+ // Weird, but play along.
+ ignore_result(fd.release());
+ } else {
+ PCHECK(fd.get() >= 0) << "open";
+ PCHECK(HANDLE_EINTR(dup2(fd.get(), desired_fd)) != -1) << "dup2";
+ fd.reset();
+ }
+}
+
+} // namespace
+
+void CloseStdinAndStdout() {
+ // Open /dev/null for stdin and stdout separately, so that it can be opened
+ // with the correct mode each time. This ensures that attempts to write to
+ // stdin or read from stdout fail with EBADF.
+ CloseStdioStream(STDIN_FILENO, O_RDONLY);
+ CloseStdioStream(STDOUT_FILENO, O_WRONLY);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/posix/close_stdio.h b/chromium/third_party/crashpad/crashpad/util/posix/close_stdio.h
new file mode 100644
index 00000000000..9515923b52d
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/posix/close_stdio.h
@@ -0,0 +1,42 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_POSIX_CLOSE_STDIO_H_
+#define CRASHPAD_UTIL_POSIX_CLOSE_STDIO_H_
+
+namespace crashpad {
+
+//! \brief Closes `stdin` and `stdout` by opening `/dev/null` over them.
+//!
+//! It is normally inadvisable to `close()` the three standard input/output
+//! streams, because they occupy special file descriptors. Closing them outright
+//! could result in their file descriptors being reused. This causes problems
+//! for library code (including the standard library) that expects these file
+//! descriptors to have special meaning.
+//!
+//! This function discards the standard input and standard output streams by
+//! opening `/dev/null` and assigning it to their file descriptors, closing
+//! whatever had been at those file descriptors previously.
+//!
+//! `stderr`, the standard error stream, is not closed. It is often useful to
+//! retain the ability to send diagnostic messages to the standard error stream.
+//!
+//! \note This function can only maintain its guarantees in a single-threaded
+//! process, or in situations where the caller has control of all threads in
+//! the process.
+void CloseStdinAndStdout();
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_POSIX_CLOSE_STDIO_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/posix/drop_privileges.cc b/chromium/third_party/crashpad/crashpad/util/posix/drop_privileges.cc
new file mode 100644
index 00000000000..3fb0ab4a562
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/posix/drop_privileges.cc
@@ -0,0 +1,91 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "build/build_config.h"
+
+namespace crashpad {
+
+void DropPrivileges() {
+ gid_t gid = getgid();
+ uid_t uid = getuid();
+
+#if defined(OS_MACOSX)
+ // Based on the POSIX.1-2008 2013 edition documentation for setreuid() and
+ // setregid(), setreuid() and setregid() alone should be sufficient to drop
+ // privileges. The standard specifies that the saved ID should be set to the
+ // effective ID whenever the real ID is not -1, or whenever the effective ID
+ // is set not equal to the real ID. This code never specifies -1, so the
+ // setreuid() and setregid() alone should work according to the standard.
+ //
+ // In practice, on Mac OS X, setuid() and setgid() (or seteuid() and
+ // setegid()) must be called first. Otherwise, setreuid() and setregid() do
+ // not alter the saved IDs, leaving open the possibility for future privilege
+ // escalation.
+ //
+ // The problem exists in 10.9.5 xnu-2422.115.4/bsd/kern/kern_prot.c
+ // setreuid(). Based on its comments, it purports to set the svuid to the new
+ // euid when the old svuid doesn’t match one of the new ruid and euid. This
+ // isn’t how POSIX.1-2008 says it should behave, but it should work for this
+ // function’s purposes. In reality, setreuid() doesn’t even do this: it sets
+ // the svuid to the old euid, which does not drop privileges when the old euid
+ // is different from the desired euid. The workaround of calling setuid() or
+ // seteuid() before setreuid() works because it sets the euid so that by the
+ // time setreuid() runs, the old euid is actually the value that ought to be
+ // set as the svuid. setregid() is similar. This bug is filed as radar
+ // 18987552.
+ //
+ // setuid() and setgid() alone will only set the saved IDs when running as
+ // root. When running a setuid non-root or setgid program, they do not alter
+ // the saved ID, and do not effect a permanent privilege drop.
+ gid_t egid = getegid();
+ PCHECK(setgid(gid) == 0) << "setgid";
+ PCHECK(setregid(gid, gid) == 0) << "setregid";
+
+ uid_t euid = geteuid();
+ PCHECK(setuid(uid) == 0) << "setuid";
+ PCHECK(setreuid(uid, uid) == 0) << "setreuid";
+
+ if (uid != 0) {
+ // Because the setXid()+setreXid() interface to change IDs is fragile,
+ // ensure that privileges cannot be regained. This can only be done if the
+ // real user ID (and now the effective user ID as well) is not root, because
+ // root always has permission to change identity.
+ if (euid != uid) {
+ CHECK_EQ(seteuid(euid), -1);
+ }
+ if (egid != gid) {
+ CHECK_EQ(setegid(egid), -1);
+ }
+ }
+#elif defined(OS_LINUX)
+ PCHECK(setresgid(gid, gid, gid) == 0) << "setresgid";
+ PCHECK(setresuid(uid, uid, uid) == 0) << "setresuid";
+
+ // Don’t check to see if privileges can be regained on Linux, because on
+ // Linux, it’s not as simple as ensuring that this can’t be done if non-root.
+ // Instead, the ability to change user and group IDs are controlled by the
+ // CAP_SETUID and CAP_SETGID capabilities, which may be granted to non-root
+ // processes. Since the setresXid() interface is well-defined, it shouldn’t be
+ // necessary to perform any additional checking anyway.
+ //
+ // TODO(mark): Drop CAP_SETUID and CAP_SETGID if present and non-root?
+#else
+#error Port this function to your system.
+#endif
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/posix/drop_privileges.h b/chromium/third_party/crashpad/crashpad/util/posix/drop_privileges.h
new file mode 100644
index 00000000000..91220e9785a
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/posix/drop_privileges.h
@@ -0,0 +1,40 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_POSIX_DROP_PRIVILEGES_H_
+#define CRASHPAD_UTIL_POSIX_DROP_PRIVILEGES_H_
+
+namespace crashpad {
+
+//! \brief Permanently drops privileges conferred by being a setuid or setgid
+//! executable.
+//!
+//! The effective user ID and saved set-user ID are set to the real user ID,
+//! negating any effects of being a setuid executable. The effective group ID
+//! and saved set-group ID are set to the real group ID, negating any effects of
+//! being a setgid executable. Because the saved set-user ID and saved set-group
+//! ID are reset, there is no way to restore the prior privileges, and the drop
+//! is permanent.
+//!
+//! This function drops privileges correctly when running setuid root and in
+//! other circumstances, including when running setuid non-root. If the program
+//! is not a setuid or setgid executable, this function has no effect.
+//!
+//! No changes are made to the supplementary group list, which is normally not
+//! altered for setuid or setgid executables.
+void DropPrivileges();
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_POSIX_DROP_PRIVILEGES_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/posix/process_info.h b/chromium/third_party/crashpad/crashpad/util/posix/process_info.h
new file mode 100644
index 00000000000..4761cf3694e
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/posix/process_info.h
@@ -0,0 +1,152 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_POSIX_PROCESS_INFO_H_
+#define CRASHPAD_UTIL_POSIX_PROCESS_INFO_H_
+
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "build/build_config.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+#if defined(OS_MACOSX)
+#include <mach/mach.h>
+#endif
+
+namespace crashpad {
+
+class ProcessInfo {
+ public:
+ ProcessInfo();
+ ~ProcessInfo();
+
+ //! \brief Initializes this object with information about the process whose ID
+ //! is \a pid.
+ //!
+ //! This method must be called successfully prior to calling any other method
+ //! in this class. This method may only be called once.
+ //!
+ //! It is unspecified whether the information that an object of this class
+ //! returns is loaded at the time Initialize() is called or subsequently, and
+ //! whether this information is cached in the object or not.
+ //!
+ //! \param[in] pid The process ID to obtain information for.
+ //!
+ //! \return `true` on success, `false` on failure with a message logged.
+ bool Initialize(pid_t pid);
+
+#if defined(OS_MACOSX) || DOXYGEN
+ //! \brief Initializes this object with information about a process based on
+ //! its Mach task.
+ //!
+ //! This method serves as a stand-in for Initialize() and may be called in its
+ //! place with the same restrictions and considerations.
+ //!
+ //! \param[in] task The Mach task to obtain information for.
+ //!
+ //! \return `true` on success, `false` on failure with an message logged.
+ bool InitializeFromTask(task_t task);
+#endif
+
+ //! \return The target task’s process ID.
+ pid_t ProcessID() const;
+
+ //! \return The target task’s parent process ID.
+ pid_t ParentProcessID() const;
+
+ //! \return The target process’ real user ID as would be returned to it by
+ //! `getuid()`.
+ uid_t RealUserID() const;
+
+ //! \return The target process’ effective user ID as would be returned to it
+ //! by `geteuid()`.
+ uid_t EffectiveUserID() const;
+
+ //! \return The target process’ saved set-user ID.
+ uid_t SavedUserID() const;
+
+ //! \return the target process’ real group ID as would be returned to it by
+ //! `getgid()`.
+ gid_t RealGroupID() const;
+
+ //! \return the target process’ effective group ID as would be returned to it
+ //! by `getegid()`.
+ gid_t EffectiveGroupID() const;
+
+ //! \return The target process’ saved set-group ID.
+ gid_t SavedGroupID() const;
+
+ //! \return the target process’ supplementary group list as would be returned
+ //! to it by `getgroups()`.
+ std::set<gid_t> SupplementaryGroups() const;
+
+ //! \return All groups that the target process claims membership in, including
+ //! RealGroupID(), EffectiveGroupID(), SavedGroupID(), and
+ //! SupplementaryGroups().
+ std::set<gid_t> AllGroups() const;
+
+ //! \brief Determines whether the target process has changed privileges.
+ //!
+ //! A process is considered to have changed privileges if it has changed its
+ //! real, effective, or saved set-user or group IDs with the `setuid()`,
+ //! `seteuid()`, `setreuid()`, `setgid()`, `setegid()`, or `setregid()` system
+ //! calls since its most recent `execve()`, or if its privileges changed at
+ //! `execve()` as a result of executing a setuid or setgid executable.
+ bool DidChangePrivileges() const;
+
+ //! \return `true` if the target task is a 64-bit process.
+ bool Is64Bit() const;
+
+ //! \brief Determines the target process’ start time.
+ //!
+ //! \param[out] start_time The time that the process started.
+ void StartTime(timeval* start_time) const;
+
+ //! \brief Obtains the arguments used to launch a process.
+ //!
+ //! Whether it is possible to obtain this information for a process with
+ //! different privileges than the running program is system-dependent.
+ //!
+ //! \param[out] argv The process’ arguments as passed to its `main()` function
+ //! as the \a argv parameter, possibly modified by the process.
+ //!
+ //! \return `true` on success, with \a argv populated appropriately.
+ //! Otherwise, `false` with a message logged.
+ //!
+ //! \note This function may spuriously return `false` when used to examine a
+ //! process that it is calling `exec()`. If examining such a process, call
+ //! this function in a retry loop with a small (100ns) delay to avoid an
+ //! erroneous assumption that \a pid is not running.
+ bool Arguments(std::vector<std::string>* argv) const;
+
+ private:
+#if defined(OS_MACOSX)
+ kinfo_proc kern_proc_info_;
+#endif
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessInfo);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_POSIX_PROCESS_INFO_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/posix/process_info_mac.cc b/chromium/third_party/crashpad/crashpad/util/posix/process_info_mac.cc
new file mode 100644
index 00000000000..17d205a68e5
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/posix/process_info_mac.cc
@@ -0,0 +1,233 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/posix/process_info.h"
+
+#include <string.h>
+
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+
+namespace crashpad {
+
+ProcessInfo::ProcessInfo() : kern_proc_info_(), initialized_() {
+}
+
+ProcessInfo::~ProcessInfo() {
+}
+
+bool ProcessInfo::Initialize(pid_t pid) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
+ size_t len = sizeof(kern_proc_info_);
+ if (sysctl(mib, arraysize(mib), &kern_proc_info_, &len, nullptr, 0) != 0) {
+ PLOG(ERROR) << "sysctl for pid " << pid;
+ return false;
+ }
+
+ // This sysctl does not return an error if the pid was not found. 10.9.5
+ // xnu-2422.115.4/bsd/kern/kern_sysctl.c sysctl_prochandle() calls
+ // xnu-2422.115.4/bsd/kern/kern_proc.c proc_iterate(), which provides no
+ // indication of whether anything was done. To catch this, check that the PID
+ // has changed from the 0 value it was given when initialized by the
+ // constructor.
+ if (kern_proc_info_.kp_proc.p_pid == 0) {
+ LOG(WARNING) << "pid " << pid << " not found";
+ return false;
+ }
+
+ DCHECK_EQ(kern_proc_info_.kp_proc.p_pid, pid);
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+bool ProcessInfo::InitializeFromTask(task_t task) {
+ pid_t pid;
+ kern_return_t kr = pid_for_task(task, &pid);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "pid_for_task";
+ return false;
+ }
+
+ return Initialize(pid);
+}
+
+pid_t ProcessInfo::ProcessID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return kern_proc_info_.kp_proc.p_pid;
+}
+
+pid_t ProcessInfo::ParentProcessID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return kern_proc_info_.kp_eproc.e_ppid;
+}
+
+uid_t ProcessInfo::RealUserID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return kern_proc_info_.kp_eproc.e_pcred.p_ruid;
+}
+
+uid_t ProcessInfo::EffectiveUserID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return kern_proc_info_.kp_eproc.e_ucred.cr_uid;
+}
+
+uid_t ProcessInfo::SavedUserID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return kern_proc_info_.kp_eproc.e_pcred.p_svuid;
+}
+
+gid_t ProcessInfo::RealGroupID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return kern_proc_info_.kp_eproc.e_pcred.p_rgid;
+}
+
+gid_t ProcessInfo::EffectiveGroupID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return kern_proc_info_.kp_eproc.e_ucred.cr_gid;
+}
+
+gid_t ProcessInfo::SavedGroupID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return kern_proc_info_.kp_eproc.e_pcred.p_svgid;
+}
+
+std::set<gid_t> ProcessInfo::SupplementaryGroups() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ const short ngroups = kern_proc_info_.kp_eproc.e_ucred.cr_ngroups;
+ DCHECK_GE(ngroups, 0);
+ DCHECK_LE(static_cast<size_t>(ngroups),
+ arraysize(kern_proc_info_.kp_eproc.e_ucred.cr_groups));
+
+ const gid_t* groups = kern_proc_info_.kp_eproc.e_ucred.cr_groups;
+ return std::set<gid_t>(&groups[0], &groups[ngroups]);
+}
+
+std::set<gid_t> ProcessInfo::AllGroups() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ std::set<gid_t> all_groups = SupplementaryGroups();
+ all_groups.insert(RealGroupID());
+ all_groups.insert(EffectiveGroupID());
+ all_groups.insert(SavedGroupID());
+ return all_groups;
+}
+
+bool ProcessInfo::DidChangePrivileges() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return kern_proc_info_.kp_proc.p_flag & P_SUGID;
+}
+
+bool ProcessInfo::Is64Bit() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return kern_proc_info_.kp_proc.p_flag & P_LP64;
+}
+
+void ProcessInfo::StartTime(timeval* start_time) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *start_time = kern_proc_info_.kp_proc.p_starttime;
+}
+
+bool ProcessInfo::Arguments(std::vector<std::string>* argv) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ // The format of KERN_PROCARGS2 is explained in 10.9.2 adv_cmds-153/ps/print.c
+ // getproclline(). It is an int (argc) followed by the executable’s string
+ // area. The string area consists of NUL-terminated strings, beginning with
+ // the executable path, and then starting on an aligned boundary, all of the
+ // elements of argv, envp, and applev.
+
+ // It is possible for a process to exec() in between the two sysctl() calls
+ // below. If that happens, and the string area of the new program is larger
+ // than that of the old one, args_size_estimate will be too small. To detect
+ // this situation, the second sysctl() attempts to fetch args_size_estimate +
+ // 1 bytes, expecting to only receive args_size_estimate. If it gets the extra
+ // byte, it indicates that the string area has grown, and the sysctl() pair
+ // will be retried a limited number of times.
+
+ size_t args_size_estimate;
+ size_t args_size;
+ std::string args;
+ int tries = 3;
+ const pid_t pid = ProcessID();
+ do {
+ int mib[] = {CTL_KERN, KERN_PROCARGS2, pid};
+ int rv =
+ sysctl(mib, arraysize(mib), nullptr, &args_size_estimate, nullptr, 0);
+ if (rv != 0) {
+ PLOG(ERROR) << "sysctl (size) for pid " << pid;
+ return false;
+ }
+
+ args_size = args_size_estimate + 1;
+ args.resize(args_size);
+ rv = sysctl(mib, arraysize(mib), &args[0], &args_size, nullptr, 0);
+ if (rv != 0) {
+ PLOG(ERROR) << "sysctl (data) for pid " << pid;
+ return false;
+ }
+ } while (args_size == args_size_estimate + 1 && tries--);
+
+ if (args_size == args_size_estimate + 1) {
+ LOG(ERROR) << "unexpected args_size";
+ return false;
+ }
+
+ // KERN_PROCARGS2 needs to at least contain argc.
+ if (args_size < sizeof(int)) {
+ LOG(ERROR) << "tiny args_size";
+ return false;
+ }
+ args.resize(args_size);
+
+ // Get argc.
+ int argc;
+ memcpy(&argc, &args[0], sizeof(argc));
+
+ // Find the end of the executable path.
+ size_t start_pos = sizeof(argc);
+ size_t nul_pos = args.find('\0', start_pos);
+ if (nul_pos == std::string::npos) {
+ LOG(ERROR) << "unterminated executable path";
+ return false;
+ }
+
+ // Find the beginning of the string area.
+ start_pos = args.find_first_not_of('\0', nul_pos);
+ if (start_pos == std::string::npos) {
+ LOG(ERROR) << "no string area";
+ return false;
+ }
+
+ std::vector<std::string> local_argv;
+ while (argc-- && nul_pos != std::string::npos) {
+ nul_pos = args.find('\0', start_pos);
+ local_argv.push_back(args.substr(start_pos, nul_pos - start_pos));
+ start_pos = nul_pos + 1;
+ }
+
+ if (argc >= 0) {
+ // Not every argument was recovered.
+ LOG(ERROR) << "did not recover all arguments";
+ return false;
+ }
+
+ argv->swap(local_argv);
+ return true;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/posix/process_info_test.cc b/chromium/third_party/crashpad/crashpad/util/posix/process_info_test.cc
new file mode 100644
index 00000000000..59315a0b378
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/posix/process_info_test.cc
@@ -0,0 +1,148 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/posix/process_info.h"
+
+#include <time.h>
+#include <unistd.h>
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "build/build_config.h"
+#include "gtest/gtest.h"
+#include "test/errors.h"
+
+#if defined(OS_MACOSX)
+#include <crt_externs.h>
+#endif
+
+namespace crashpad {
+namespace test {
+namespace {
+
+void TestSelfProcess(const ProcessInfo& process_info) {
+ EXPECT_EQ(getpid(), process_info.ProcessID());
+ EXPECT_EQ(getppid(), process_info.ParentProcessID());
+
+ // There’s no system call to obtain the saved set-user ID or saved set-group
+ // ID in an easy way. Normally, they are the same as the effective user ID and
+ // effective group ID, so just check against those.
+ EXPECT_EQ(getuid(), process_info.RealUserID());
+ const uid_t euid = geteuid();
+ EXPECT_EQ(euid, process_info.EffectiveUserID());
+ EXPECT_EQ(euid, process_info.SavedUserID());
+ const gid_t gid = getgid();
+ EXPECT_EQ(gid, process_info.RealGroupID());
+ const gid_t egid = getegid();
+ EXPECT_EQ(egid, process_info.EffectiveGroupID());
+ EXPECT_EQ(egid, process_info.SavedGroupID());
+
+ // Test SupplementaryGroups().
+ int group_count = getgroups(0, nullptr);
+ ASSERT_GE(group_count, 0) << ErrnoMessage("getgroups");
+
+ std::vector<gid_t> group_vector(group_count);
+ if (group_count > 0) {
+ group_count = getgroups(group_vector.size(), &group_vector[0]);
+ ASSERT_GE(group_count, 0) << ErrnoMessage("getgroups");
+ ASSERT_EQ(group_vector.size(), implicit_cast<size_t>(group_count));
+ }
+
+ std::set<gid_t> group_set(group_vector.begin(), group_vector.end());
+ EXPECT_EQ(group_set, process_info.SupplementaryGroups());
+
+ // Test AllGroups(), which is SupplementaryGroups() plus the real, effective,
+ // and saved set-group IDs. The effective and saved set-group IDs are expected
+ // to be identical (see above).
+ group_set.insert(gid);
+ group_set.insert(egid);
+
+ EXPECT_EQ(group_set, process_info.AllGroups());
+
+ // The test executable isn’t expected to change privileges.
+ EXPECT_FALSE(process_info.DidChangePrivileges());
+
+#if defined(ARCH_CPU_64_BITS)
+ EXPECT_TRUE(process_info.Is64Bit());
+#else
+ EXPECT_FALSE(process_info.Is64Bit());
+#endif
+
+ // Test StartTime(). This program must have started at some time in the past.
+ timeval start_time;
+ process_info.StartTime(&start_time);
+ time_t now;
+ time(&now);
+ EXPECT_LE(start_time.tv_sec, now);
+
+ std::vector<std::string> argv;
+ ASSERT_TRUE(process_info.Arguments(&argv));
+
+ // gtest argv processing scrambles argv, but it leaves argc and argv[0]
+ // intact, so test those.
+
+#if defined(OS_MACOSX)
+ int expect_argc = *_NSGetArgc();
+ char** expect_argv = *_NSGetArgv();
+#else
+#error Obtain expect_argc and expect_argv correctly on your system.
+#endif
+
+ int argc = implicit_cast<int>(argv.size());
+ EXPECT_EQ(expect_argc, argc);
+
+ ASSERT_GE(expect_argc, 1);
+ ASSERT_GE(argc, 1);
+
+ EXPECT_EQ(std::string(expect_argv[0]), argv[0]);
+}
+
+
+TEST(ProcessInfo, Self) {
+ ProcessInfo process_info;
+ ASSERT_TRUE(process_info.Initialize(getpid()));
+ TestSelfProcess(process_info);
+}
+
+#if defined(OS_MACOSX)
+TEST(ProcessInfo, SelfTask) {
+ ProcessInfo process_info;
+ ASSERT_TRUE(process_info.InitializeFromTask(mach_task_self()));
+ TestSelfProcess(process_info);
+}
+#endif
+
+TEST(ProcessInfo, Pid1) {
+ // PID 1 is expected to be init or the system’s equivalent. This tests reading
+ // information about another process.
+ ProcessInfo process_info;
+ ASSERT_TRUE(process_info.Initialize(1));
+
+ EXPECT_EQ(implicit_cast<pid_t>(1), process_info.ProcessID());
+ EXPECT_EQ(implicit_cast<pid_t>(0), process_info.ParentProcessID());
+ EXPECT_EQ(implicit_cast<uid_t>(0), process_info.RealUserID());
+ EXPECT_EQ(implicit_cast<uid_t>(0), process_info.EffectiveUserID());
+ EXPECT_EQ(implicit_cast<uid_t>(0), process_info.SavedUserID());
+ EXPECT_EQ(implicit_cast<gid_t>(0), process_info.RealGroupID());
+ EXPECT_EQ(implicit_cast<gid_t>(0), process_info.EffectiveGroupID());
+ EXPECT_EQ(implicit_cast<gid_t>(0), process_info.SavedGroupID());
+ EXPECT_FALSE(process_info.AllGroups().empty());
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.cc b/chromium/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.cc
new file mode 100644
index 00000000000..b7f8e8a922a
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.cc
@@ -0,0 +1,166 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/posix/symbolic_constants_posix.h"
+
+#include <sys/signal.h>
+
+#include "base/basictypes.h"
+#include "base/strings/stringprintf.h"
+#include "util/stdlib/string_number_conversion.h"
+
+namespace {
+
+const char* kSignalNames[] = {
+ nullptr,
+
+#if defined(OS_MACOSX)
+ // sed -Ene 's/^#define[[:space:]]SIG([[:alnum:]]+)[[:space:]]+[[:digit:]]{1,2}([[:space:]]|$).*/ "\1",/p'
+ // /usr/include/sys/signal.h
+ // and fix up by removing the entry for SIGPOLL.
+ "HUP",
+ "INT",
+ "QUIT",
+ "ILL",
+ "TRAP",
+ "ABRT",
+ "EMT",
+ "FPE",
+ "KILL",
+ "BUS",
+ "SEGV",
+ "SYS",
+ "PIPE",
+ "ALRM",
+ "TERM",
+ "URG",
+ "STOP",
+ "TSTP",
+ "CONT",
+ "CHLD",
+ "TTIN",
+ "TTOU",
+ "IO",
+ "XCPU",
+ "XFSZ",
+ "VTALRM",
+ "PROF",
+ "WINCH",
+ "INFO",
+ "USR1",
+ "USR2",
+#elif defined(OS_LINUX)
+ // sed -Ene 's/^#define[[:space:]]SIG([[:alnum:]]+)[[:space:]]+[[:digit:]]{1,2}([[:space:]]|$).*/ "\1",/p'
+ // /usr/include/asm-generic/signal.h
+ // and fix up by removing SIGIOT, SIGLOST, SIGUNUSED, and SIGRTMIN.
+ "HUP",
+ "INT",
+ "QUIT",
+ "ILL",
+ "TRAP",
+ "ABRT",
+ "BUS",
+ "FPE",
+ "KILL",
+ "USR1",
+ "SEGV",
+ "USR2",
+ "PIPE",
+ "ALRM",
+ "TERM",
+ "STKFLT",
+ "CHLD",
+ "CONT",
+ "STOP",
+ "TSTP",
+ "TTIN",
+ "TTOU",
+ "URG",
+ "XCPU",
+ "XFSZ",
+ "VTALRM",
+ "PROF",
+ "WINCH",
+ "IO",
+ "PWR",
+ "SYS",
+#endif
+};
+#if defined(OS_LINUX)
+// NSIG is 64 to account for real-time signals.
+static_assert(arraysize(kSignalNames) == 32, "kSignalNames length");
+#else
+static_assert(arraysize(kSignalNames) == NSIG, "kSignalNames length");
+#endif
+
+const char kSigPrefix[] = "SIG";
+
+} // namespace
+
+namespace crashpad {
+
+std::string SignalToString(int signal,
+ SymbolicConstantToStringOptions options) {
+ const char* signal_name =
+ implicit_cast<size_t>(signal) < arraysize(kSignalNames)
+ ? kSignalNames[signal]
+ : nullptr;
+ if (!signal_name) {
+ if (options & kUnknownIsNumeric) {
+ return base::StringPrintf("%d", signal);
+ }
+ return std::string();
+ }
+
+ if (options & kUseShortName) {
+ return std::string(signal_name);
+ }
+ return base::StringPrintf("%s%s", kSigPrefix, signal_name);
+}
+
+bool StringToSignal(const base::StringPiece& string,
+ StringToSymbolicConstantOptions options,
+ int* signal) {
+ if ((options & kAllowFullName) || (options & kAllowShortName)) {
+ bool can_match_full =
+ (options & kAllowFullName) &&
+ string.substr(0, strlen(kSigPrefix)).compare(kSigPrefix) == 0;
+ base::StringPiece short_string =
+ can_match_full ? string.substr(strlen(kSigPrefix)) : string;
+ for (int index = 0;
+ index < implicit_cast<int>(arraysize(kSignalNames));
+ ++index) {
+ const char* signal_name = kSignalNames[index];
+ if (!signal_name) {
+ continue;
+ }
+ if (can_match_full && short_string.compare(signal_name) == 0) {
+ *signal = index;
+ return true;
+ }
+ if ((options & kAllowShortName) && string.compare(signal_name) == 0) {
+ *signal = index;
+ return true;
+ }
+ }
+ }
+
+ if (options & kAllowNumber) {
+ return StringToNumber(string, signal);
+ }
+
+ return false;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.h b/chromium/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.h
new file mode 100644
index 00000000000..4fb4a358aa5
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.h
@@ -0,0 +1,48 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_POSIX_SYMBOLIC_CONSTANTS_POSIX_H_
+#define CRASHPAD_UTIL_POSIX_SYMBOLIC_CONSTANTS_POSIX_H_
+
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "util/misc/symbolic_constants_common.h"
+
+namespace crashpad {
+
+//! \brief Converts a POSIX signal value to a textual representation.
+//!
+//! \param[in] signal The signal value to convert.
+//! \param[in] options Options affecting the conversion. ::kUseOr is ignored.
+//! For ::kUnknownIsNumeric, the format is `"%d"`.
+//!
+//! \return The converted string.
+std::string SignalToString(int signal, SymbolicConstantToStringOptions options);
+
+//! \brief Converts a string to its corresponding POSIX signal value.
+//!
+//! \param[in] string The string to convert.
+//! \param[in] options Options affecting the conversion. ::kAllowOr is ignored.
+//! \param[out] signal The converted POSIX signal value.
+//!
+//! \return `true` on success, `false` if \a string could not be converted as
+//! requested.
+bool StringToSignal(const base::StringPiece& string,
+ StringToSymbolicConstantOptions options,
+ int* signal);
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_POSIX_SYMBOLIC_CONSTANTS_POSIX_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix_test.cc b/chromium/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix_test.cc
new file mode 100644
index 00000000000..db3193d45e1
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix_test.cc
@@ -0,0 +1,256 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/posix/symbolic_constants_posix.h"
+
+#include <sys/signal.h>
+
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+
+#define NUL_TEST_DATA(string) { string, arraysize(string) - 1 }
+
+namespace crashpad {
+namespace test {
+namespace {
+
+const struct {
+ int signal;
+ const char* full_name;
+ const char* short_name;
+} kSignalTestData[] = {
+ {SIGABRT, "SIGABRT", "ABRT"},
+ {SIGALRM, "SIGALRM", "ALRM"},
+ {SIGBUS, "SIGBUS", "BUS"},
+ {SIGCHLD, "SIGCHLD", "CHLD"},
+ {SIGCONT, "SIGCONT", "CONT"},
+ {SIGFPE, "SIGFPE", "FPE"},
+ {SIGHUP, "SIGHUP", "HUP"},
+ {SIGILL, "SIGILL", "ILL"},
+ {SIGINT, "SIGINT", "INT"},
+ {SIGIO, "SIGIO", "IO"},
+ {SIGKILL, "SIGKILL", "KILL"},
+ {SIGPIPE, "SIGPIPE", "PIPE"},
+ {SIGPROF, "SIGPROF", "PROF"},
+ {SIGQUIT, "SIGQUIT", "QUIT"},
+ {SIGSEGV, "SIGSEGV", "SEGV"},
+ {SIGSTOP, "SIGSTOP", "STOP"},
+ {SIGSYS, "SIGSYS", "SYS"},
+ {SIGTERM, "SIGTERM", "TERM"},
+ {SIGTRAP, "SIGTRAP", "TRAP"},
+ {SIGTSTP, "SIGTSTP", "TSTP"},
+ {SIGTTIN, "SIGTTIN", "TTIN"},
+ {SIGTTOU, "SIGTTOU", "TTOU"},
+ {SIGURG, "SIGURG", "URG"},
+ {SIGUSR1, "SIGUSR1", "USR1"},
+ {SIGUSR2, "SIGUSR2", "USR2"},
+ {SIGVTALRM, "SIGVTALRM", "VTALRM"},
+ {SIGWINCH, "SIGWINCH", "WINCH"},
+ {SIGXCPU, "SIGXCPU", "XCPU"},
+#if defined(OS_MACOSX)
+ {SIGEMT, "SIGEMT", "EMT"},
+ {SIGINFO, "SIGINFO", "INFO"},
+#elif defined(OS_LINUX)
+ {SIGPWR, "SIGPWR", "PWR"},
+ {SIGSTKFLT, "SIGSTKFLT", "STKFLT"},
+#endif
+};
+
+// If |expect| is nullptr, the conversion is expected to fail. If |expect| is
+// empty, the conversion is expected to succeed, but the precise returned string
+// value is unknown. Otherwise, the conversion is expected to succeed, and
+// |expect| contains the precise expected string value to be returned.
+//
+// Only set kUseFullName or kUseShortName when calling this. Other options are
+// exercised directly by this function.
+void TestSignalToStringOnce(int value,
+ const char* expect,
+ SymbolicConstantToStringOptions options) {
+ std::string actual = SignalToString(value, options | kUnknownIsEmpty);
+ std::string actual_numeric =
+ SignalToString(value, options | kUnknownIsNumeric);
+ if (expect) {
+ if (expect[0] == '\0') {
+ EXPECT_FALSE(actual.empty()) << "signal " << value;
+ } else {
+ EXPECT_EQ(expect, actual) << "signal " << value;
+ }
+ EXPECT_EQ(actual, actual_numeric) << "signal " << value;
+ } else {
+ EXPECT_TRUE(actual.empty()) << "signal " << value << ", actual " << actual;
+ EXPECT_FALSE(actual_numeric.empty())
+ << "signal " << value << ", actual_numeric " << actual_numeric;
+ }
+}
+
+void TestSignalToString(int value,
+ const char* expect_full,
+ const char* expect_short) {
+ {
+ SCOPED_TRACE("full_name");
+ TestSignalToStringOnce(value, expect_full, kUseFullName);
+ }
+
+ {
+ SCOPED_TRACE("short_name");
+ TestSignalToStringOnce(value, expect_short, kUseShortName);
+ }
+}
+
+TEST(SymbolicConstantsPOSIX, SignalToString) {
+ for (size_t index = 0; index < arraysize(kSignalTestData); ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %zu", index));
+ TestSignalToString(kSignalTestData[index].signal,
+ kSignalTestData[index].full_name,
+ kSignalTestData[index].short_name);
+ }
+
+#if defined(OS_LINUX)
+ // NSIG is 64 to account for real-time signals.
+ const int kSignalCount = 32;
+#else
+ const int kSignalCount = NSIG;
+#endif
+
+ for (int signal = 0; signal < kSignalCount + 8; ++signal) {
+ SCOPED_TRACE(base::StringPrintf("signal %d", signal));
+ if (signal > 0 && signal < kSignalCount) {
+ TestSignalToString(signal, "", "");
+ } else {
+ TestSignalToString(signal, nullptr, nullptr);
+ }
+ }
+}
+
+void TestStringToSignal(const base::StringPiece& string,
+ StringToSymbolicConstantOptions options,
+ bool expect_result,
+ int expect_value) {
+ int actual_value;
+ bool actual_result = StringToSignal(string, options, &actual_value);
+ if (expect_result) {
+ EXPECT_TRUE(actual_result) << "string " << string << ", options " << options
+ << ", signal " << expect_value;
+ if (actual_result) {
+ EXPECT_EQ(expect_value, actual_value) << "string " << string
+ << ", options " << options;
+ }
+ } else {
+ EXPECT_FALSE(actual_result) << "string " << string << ", options "
+ << options << ", signal " << actual_value;
+ }
+}
+
+TEST(SymbolicConstantsPOSIX, StringToSignal) {
+ const StringToSymbolicConstantOptions kOptions[] = {
+ 0,
+ kAllowFullName,
+ kAllowShortName,
+ kAllowFullName | kAllowShortName,
+ kAllowNumber,
+ kAllowFullName | kAllowNumber,
+ kAllowShortName | kAllowNumber,
+ kAllowFullName | kAllowShortName | kAllowNumber,
+ };
+
+ for (size_t option_index = 0;
+ option_index < arraysize(kOptions);
+ ++option_index) {
+ SCOPED_TRACE(base::StringPrintf("option_index %zu", option_index));
+ StringToSymbolicConstantOptions options = kOptions[option_index];
+ for (size_t index = 0; index < arraysize(kSignalTestData); ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %zu", index));
+ int signal = kSignalTestData[index].signal;
+ {
+ SCOPED_TRACE("full_name");
+ TestStringToSignal(kSignalTestData[index].full_name,
+ options,
+ options & kAllowFullName,
+ signal);
+ }
+ {
+ SCOPED_TRACE("short_name");
+ TestStringToSignal(kSignalTestData[index].short_name,
+ options,
+ options & kAllowShortName,
+ signal);
+ }
+ {
+ SCOPED_TRACE("number");
+ std::string number_string = base::StringPrintf("%d", signal);
+ TestStringToSignal(
+ number_string, options, options & kAllowNumber, signal);
+ }
+ }
+
+ const char* const kNegativeTestData[] = {
+ "SIGHUP ",
+ " SIGINT",
+ "QUIT ",
+ " ILL",
+ "SIGSIGTRAP",
+ "SIGABRTRON",
+ "FPES",
+ "SIGGARBAGE",
+ "random",
+ "",
+ };
+
+ for (size_t index = 0; index < arraysize(kNegativeTestData); ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %zu", index));
+ TestStringToSignal(kNegativeTestData[index], options, false, 0);
+ }
+
+ const struct {
+ const char* string;
+ size_t length;
+ } kNULTestData[] = {
+ NUL_TEST_DATA("\0SIGBUS"),
+ NUL_TEST_DATA("SIG\0BUS"),
+ NUL_TEST_DATA("SIGB\0US"),
+ NUL_TEST_DATA("SIGBUS\0"),
+ NUL_TEST_DATA("\0BUS"),
+ NUL_TEST_DATA("BUS\0"),
+ NUL_TEST_DATA("B\0US"),
+ NUL_TEST_DATA("\0002"),
+ NUL_TEST_DATA("2\0"),
+ NUL_TEST_DATA("1\0002"),
+ };
+
+ for (size_t index = 0; index < arraysize(kNULTestData); ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %zu", index));
+ base::StringPiece string(kNULTestData[index].string,
+ kNULTestData[index].length);
+ TestStringToSignal(string, options, false, 0);
+ }
+ }
+
+ // Ensure that a NUL is not required at the end of the string.
+ {
+ SCOPED_TRACE("trailing_NUL_full");
+ TestStringToSignal(
+ base::StringPiece("SIGBUST", 6), kAllowFullName, true, SIGBUS);
+ }
+ {
+ SCOPED_TRACE("trailing_NUL_short");
+ TestStringToSignal(
+ base::StringPiece("BUST", 3), kAllowShortName, true, SIGBUS);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/stdlib/cxx.h b/chromium/third_party/crashpad/crashpad/util/stdlib/cxx.h
new file mode 100644
index 00000000000..0e1cc4350f2
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/stdlib/cxx.h
@@ -0,0 +1,70 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_STDLIB_CXX_H_
+#define CRASHPAD_UTIL_STDLIB_CXX_H_
+
+#include "build/build_config.h"
+
+#if defined(COMPILER_MSVC)
+
+#define CXX_LIBRARY_VERSION 2011
+#define CXX_LIBRARY_HAS_CONSTEXPR 0
+
+#else // !COMPILER_MSVC
+
+// <ciso646> doesn’t do very much, and under libc++, it will cause the
+// _LIBCPP_VERSION macro to be defined properly. Under libstdc++, it doesn’t
+// cause __GLIBCXX__ to be defined, but if _LIBCPP_VERSION isn’t defined after
+// #including <ciso646>, assume libstdc++ and #include libstdc++’s internal
+// header that defines __GLIBCXX__.
+
+#include <ciso646>
+
+#if !defined(_LIBCPP_VERSION)
+#if defined(__has_include)
+#if __has_include(<bits/c++config.h>)
+#include <bits/c++config.h>
+#endif
+#else
+#include <bits/c++config.h>
+#endif
+#endif
+
+// libstdc++ does not identify its version directly, but identifies the GCC
+// package it is a part of via the __GLIBCXX__ macro, which is based on the date
+// of the GCC release. libstdc++ had sufficient C++11 support as of GCC 4.6.0,
+// __GLIBCXX__ 20110325, but maintenance releases in the 4.4 an 4.5 series
+// followed this date, so check for those versions by their date stamps.
+// http://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html#abi.versioning
+//
+// libc++, identified by _LIBCPP_VERSION, always supports C++11.
+#if __cplusplus >= 201103l && \
+ ((defined(__GLIBCXX__) && \
+ __GLIBCXX__ >= 20110325ul && /* GCC >= 4.6.0 */ \
+ __GLIBCXX__ != 20110416ul && /* GCC 4.4.6 */ \
+ __GLIBCXX__ != 20120313ul && /* GCC 4.4.7 */ \
+ __GLIBCXX__ != 20110428ul && /* GCC 4.5.3 */ \
+ __GLIBCXX__ != 20120702ul) || /* GCC 4.5.4 */ \
+ (defined(_LIBCPP_VERSION)))
+#define CXX_LIBRARY_VERSION 2011
+#define CXX_LIBRARY_HAS_CONSTEXPR 1
+#else
+#define CXX_LIBRARY_VERSION 2003
+#define CXX_LIBRARY_HAS_CONSTEXPR 0
+#endif
+
+#endif // COMPILER_MSVC
+
+#endif // CRASHPAD_UTIL_STDLIB_CXX_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/stdlib/map_insert.h b/chromium/third_party/crashpad/crashpad/util/stdlib/map_insert.h
new file mode 100644
index 00000000000..956271b32ad
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/stdlib/map_insert.h
@@ -0,0 +1,56 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_STDLIB_MAP_INSERT_H_
+#define CRASHPAD_UTIL_STDLIB_MAP_INSERT_H_
+
+#include <map>
+#include <utility>
+
+namespace crashpad {
+
+//! \brief Inserts a mapping from \a key to \a value into \a map, or replaces
+//! an existing mapping so that \a key maps to \value.
+//!
+//! This behaves similarly to `std::map<>::insert_or_assign()` proposed for
+//! C++17, except that the \a old_value parameter is added.
+//!
+//! \param[inout] map The map to operate on.
+//! \param[in] key The key that should be mapped to \a value.
+//! \param[in] value The value that \a key should map to.
+//! \param[out] old_value If \a key was previously present in \a map, this will
+//! be set to its previous value. This parameter is optional and may be
+//! `nullptr` if this information is not required.
+//!
+//! \return `false` if \a key was previously present in \a map. If \a old_value
+//! is not `nullptr`, it will be set to the previous value. `true` if \a
+//! key was not present in the map and was inserted.
+template <typename T>
+bool MapInsertOrReplace(T* map,
+ const typename T::key_type& key,
+ const typename T::mapped_type& value,
+ typename T::mapped_type* old_value) {
+ const auto result = map->insert(std::make_pair(key, value));
+ if (!result.second) {
+ if (old_value) {
+ *old_value = result.first->second;
+ }
+ result.first->second = value;
+ }
+ return result.second;
+}
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_STDLIB_MAP_INSERT_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/stdlib/map_insert_test.cc b/chromium/third_party/crashpad/crashpad/util/stdlib/map_insert_test.cc
new file mode 100644
index 00000000000..d3197b96aa5
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/stdlib/map_insert_test.cc
@@ -0,0 +1,54 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/stdlib/map_insert.h"
+
+#include <string>
+
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(MapInsert, MapInsertOrReplace) {
+ std::map<std::string, int> map;
+ int old_value;
+ EXPECT_TRUE(MapInsertOrReplace(&map, "key", 1, &old_value));
+ std::map<std::string, int> expect_map;
+ expect_map["key"] = 1;
+ EXPECT_EQ(expect_map, map);
+
+ EXPECT_FALSE(MapInsertOrReplace(&map, "key", 2, &old_value));
+ EXPECT_EQ(1, old_value);
+ expect_map["key"] = 2;
+ EXPECT_EQ(expect_map, map);
+
+ EXPECT_TRUE(MapInsertOrReplace(&map, "another", 3, &old_value));
+ expect_map["another"] = 3;
+ EXPECT_EQ(expect_map, map);
+
+ // Make sure nullptr is accepted as old_value.
+ EXPECT_TRUE(MapInsertOrReplace(&map, "yet another", 5, nullptr));
+ expect_map["yet another"] = 5;
+ EXPECT_EQ(expect_map, map);
+
+ EXPECT_FALSE(MapInsertOrReplace(&map, "yet another", 6, nullptr));
+ expect_map["yet another"] = 6;
+ EXPECT_EQ(expect_map, map);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/stdlib/objc.h b/chromium/third_party/crashpad/crashpad/util/stdlib/objc.h
new file mode 100644
index 00000000000..0b44bbab0db
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/stdlib/objc.h
@@ -0,0 +1,39 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_STDLIB_OBJC_H_
+#define CRASHPAD_UTIL_STDLIB_OBJC_H_
+
+#include <AvailabilityMacros.h>
+#include <objc/objc.h>
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_8
+
+// In order for the @NO and @YES literals to work, NO and YES must be defined as
+// __objc_no and __objc_yes. See
+// http://llvm.org/releases/3.1/tools/clang/docs/ObjectiveCLiterals.html .
+//
+// NO and YES are defined properly for this purpose in the 10.8 SDK, but not in
+// earlier SDKs. Because this code is never expected to be compiled with a
+// compiler that does not understand the modern forms of these boolean
+// constants, but it may be built with an older SDK, replace the outdated SDK
+// definitions unconditionally.
+#undef NO
+#undef YES
+#define NO __objc_no
+#define YES __objc_yes
+
+#endif
+
+#endif // CRASHPAD_UTIL_STDLIB_OBJC_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/stdlib/pointer_container.h b/chromium/third_party/crashpad/crashpad/util/stdlib/pointer_container.h
new file mode 100644
index 00000000000..cb3e83643fd
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/stdlib/pointer_container.h
@@ -0,0 +1,57 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_STDLIB_POINTER_CONTAINER_H_
+#define CRASHPAD_UTIL_STDLIB_POINTER_CONTAINER_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/stl_util.h"
+
+namespace crashpad {
+
+//! \brief Allows a standard container to “own” pointer elements stored in it.
+//!
+//! When the container is destroyed, `delete` will be called on its pointer
+//! elements.
+//!
+//! \note No attempt is made to `delete` elements that are removed from the
+//! container by other means, such as replacement or `clear()`.
+template <typename ContainerType>
+class PointerContainer : public ContainerType {
+ public:
+ PointerContainer() : ContainerType(), pointer_deleter_(this) {}
+
+ ~PointerContainer() {}
+
+ private:
+ STLElementDeleter<ContainerType> pointer_deleter_;
+
+ DISALLOW_COPY_AND_ASSIGN(PointerContainer);
+};
+
+//! \brief Allows a `std::vector` to “own” pointer elements stored in it.
+//!
+//! When the vector is destroyed, `delete` will be called on its pointer
+//! elements.
+//!
+//! \note No attempt is made to `delete` elements that are removed from the
+//! vector by other means, such as replacement or `clear()`.
+template <typename T>
+using PointerVector = PointerContainer<std::vector<T*>>;
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_STDLIB_POINTER_CONTAINER_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.cc b/chromium/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.cc
new file mode 100644
index 00000000000..8ab949efeaf
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.cc
@@ -0,0 +1,156 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/stdlib/string_number_conversion.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <limits>
+
+#include "base/logging.h"
+#include "util/stdlib/cxx.h"
+
+// CONSTEXPR_STATIC_ASSERT will be a normal static_assert if the C++ library is
+// the C++11 library. If using an older C++ library, the
+// std::numeric_limits<>::min() and max() functions will not be marked as
+// constexpr, and thus won’t be usable with static_assert(). In that case, a
+// run-time CHECK() will have to do.
+#if CXX_LIBRARY_HAS_CONSTEXPR
+#define CONSTEXPR_STATIC_ASSERT(condition, message) \
+ static_assert(condition, message)
+#else
+#define CONSTEXPR_STATIC_ASSERT(condition, message) CHECK(condition) << message
+#endif
+
+namespace {
+
+template <typename TIntType, typename TLongType>
+struct StringToIntegerTraits {
+ using IntType = TIntType;
+ using LongType = TLongType;
+ static void TypeCheck() {
+ static_assert(std::numeric_limits<TIntType>::is_integer &&
+ std::numeric_limits<TLongType>::is_integer,
+ "IntType and LongType must be integer");
+ static_assert(std::numeric_limits<TIntType>::is_signed ==
+ std::numeric_limits<TLongType>::is_signed,
+ "IntType and LongType signedness must agree");
+ CONSTEXPR_STATIC_ASSERT(std::numeric_limits<TIntType>::min() >=
+ std::numeric_limits<TLongType>::min() &&
+ std::numeric_limits<TIntType>::min() <
+ std::numeric_limits<TLongType>::max(),
+ "IntType min must be in LongType range");
+ CONSTEXPR_STATIC_ASSERT(std::numeric_limits<TIntType>::max() >
+ std::numeric_limits<TLongType>::min() &&
+ std::numeric_limits<TIntType>::max() <=
+ std::numeric_limits<TLongType>::max(),
+ "IntType max must be in LongType range");
+ }
+};
+
+template <typename TIntType, typename TLongType>
+struct StringToSignedIntegerTraits
+ : public StringToIntegerTraits<TIntType, TLongType> {
+ static void TypeCheck() {
+ static_assert(std::numeric_limits<TIntType>::is_signed,
+ "StringToSignedTraits IntType must be signed");
+ return super::TypeCheck();
+ }
+ static bool IsNegativeOverflow(TLongType value) {
+ return value < std::numeric_limits<TIntType>::min();
+ }
+
+ private:
+ using super = StringToIntegerTraits<TIntType, TLongType>;
+};
+
+template <typename TIntType, typename TLongType>
+struct StringToUnsignedIntegerTraits
+ : public StringToIntegerTraits<TIntType, TLongType> {
+ static void TypeCheck() {
+ static_assert(!std::numeric_limits<TIntType>::is_signed,
+ "StringToUnsignedTraits IntType must be unsigned");
+ return super::TypeCheck();
+ }
+ static bool IsNegativeOverflow(TLongType value) { return false; }
+
+ private:
+ using super = StringToIntegerTraits<TIntType, TLongType>;
+};
+
+struct StringToIntTraits : public StringToSignedIntegerTraits<int, long> {
+ static LongType Convert(const char* str, char** end, int base) {
+ return strtol(str, end, base);
+ }
+};
+
+struct StringToUnsignedIntTraits
+ : public StringToUnsignedIntegerTraits<unsigned int, unsigned long> {
+ static LongType Convert(const char* str, char** end, int base) {
+ if (str[0] == '-') {
+ return 0;
+ }
+ return strtoul(str, end, base);
+ }
+};
+
+template <typename Traits>
+bool StringToIntegerInternal(const base::StringPiece& string,
+ typename Traits::IntType* number) {
+ using IntType = typename Traits::IntType;
+ using LongType = typename Traits::LongType;
+
+ Traits::TypeCheck();
+
+ if (string.empty() || isspace(string[0])) {
+ return false;
+ }
+
+ if (string[string.length()] != '\0') {
+ // The implementations use the C standard library’s conversion routines,
+ // which rely on the strings having a trailing NUL character. std::string
+ // will NUL-terminate.
+ std::string terminated_string(string.data(), string.length());
+ return StringToIntegerInternal<Traits>(terminated_string, number);
+ }
+
+ errno = 0;
+ char* end;
+ LongType result = Traits::Convert(string.data(), &end, 0);
+ if (Traits::IsNegativeOverflow(result) ||
+ result > std::numeric_limits<IntType>::max() ||
+ errno == ERANGE ||
+ end != string.data() + string.length()) {
+ return false;
+ }
+ *number = result;
+ return true;
+}
+
+} // namespace
+
+namespace crashpad {
+
+bool StringToNumber(const base::StringPiece& string, int* number) {
+ return StringToIntegerInternal<StringToIntTraits>(string, number);
+}
+
+bool StringToNumber(const base::StringPiece& string, unsigned int* number) {
+ return StringToIntegerInternal<StringToUnsignedIntTraits>(string, number);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.h b/chromium/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.h
new file mode 100644
index 00000000000..dbc4611e9f3
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/stdlib/string_number_conversion.h
@@ -0,0 +1,63 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_STDLIB_STRING_NUMBER_CONVERSION_H_
+#define CRASHPAD_UTIL_STDLIB_STRING_NUMBER_CONVERSION_H_
+
+#include "base/strings/string_piece.h"
+
+namespace crashpad {
+
+// Convert between strings and numbers.
+//
+// These functions will only set *number if a perfect conversion can be
+// performed. A perfect conversion contains no leading or trailing characters
+// (including whitespace) other than the number to convert, and does not
+// overflow the targeted data type. If a perfect conversion is possible, *number
+// is set and these functions return true. Otherwise, they return false.
+//
+// The interface in base/strings/string_number_conversions.h doesn’t allow
+// arbitrary bases based on whether the string begins with prefixes such as "0x"
+// as strtol does with base = 0. The functions here are implemented on the
+// strtol family with base = 0, and thus do accept such input.
+
+//! \{
+//! \brief Convert a string to a number.
+//!
+//! A conversion will only be performed if it can be done perfectly: if \a
+//! string contains no leading or trailing characters (including whitespace)
+//! other than the number to convert, and does not overflow the targeted data
+//! type.
+//!
+//! \param[in] string The string to convert to a number. As in `strtol()` with a
+//! `base` parameter of `0`, the string is treated as decimal unless it
+//! begins with a `"0x"` or `"0X"` prefix, in which case it is treated as
+//! hexadecimal, or a `"0"` prefix, in which case it is treated as octal.
+//! \param[out] number The converted number. This will only be set if a perfect
+//! conversion can be performed.
+//!
+//! \return `true` if a perfect conversion could be performed, with \a number
+//! set appropriately. `false` if a perfect conversion was not possible.
+//!
+//! \note The interface in `base/strings/string_number_conversions.h` doesn’t
+//! allow arbitrary bases based on whether the string begins with a prefix
+//! indicating its base. The functions here are provided for situations
+//! where such prefix recognition is desirable.
+bool StringToNumber(const base::StringPiece& string, int* number);
+bool StringToNumber(const base::StringPiece& string, unsigned int* number);
+//! \}
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_STDLIB_STRING_NUMBER_CONVERSION_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/stdlib/string_number_conversion_test.cc b/chromium/third_party/crashpad/crashpad/util/stdlib/string_number_conversion_test.cc
new file mode 100644
index 00000000000..97d3152bbae
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/stdlib/string_number_conversion_test.cc
@@ -0,0 +1,224 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/stdlib/string_number_conversion.h"
+
+#include "base/basictypes.h"
+#include "gtest/gtest.h"
+
+#include <limits>
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(StringNumberConversion, StringToInt) {
+ const struct {
+ const char* string;
+ bool valid;
+ int value;
+ } kTestData[] = {
+ {"", false, 0},
+ {"0", true, 0},
+ {"1", true, 1},
+ {"2147483647", true, std::numeric_limits<int>::max()},
+ {"2147483648", false, 0},
+ {"4294967295", false, 0},
+ {"4294967296", false, 0},
+ {"-1", true, -1},
+ {"-2147483648", true, std::numeric_limits<int>::min()},
+ {"-2147483649", false, 0},
+ {"00", true, 0},
+ {"01", true, 1},
+ {"-01", true, -1},
+ {"+2", true, 2},
+ {"0x10", true, 16},
+ {"-0x10", true, -16},
+ {"+0x20", true, 32},
+ {"0xf", true, 15},
+ {"0xg", false, 0},
+ {"0x7fffffff", true, std::numeric_limits<int>::max()},
+ {"0x7FfFfFfF", true, std::numeric_limits<int>::max()},
+ {"0x80000000", false, 0},
+ {"0xFFFFFFFF", false, 0},
+ {"-0x7fffffff", true, -2147483647},
+ {"-0x80000000", true, std::numeric_limits<int>::min()},
+ {"-0x80000001", false, 0},
+ {"-0xffffffff", false, 0},
+ {"0x100000000", false, 0},
+ {"0xabcdef", true, 11259375},
+ {"010", true, 8},
+ {"-010", true, -8},
+ {"+020", true, 16},
+ {"07", true, 7},
+ {"08", false, 0},
+ {" 0", false, 0},
+ {"0 ", false, 0},
+ {" 0 ", false, 0},
+ {" 1", false, 0},
+ {"1 ", false, 0},
+ {" 1 ", false, 0},
+ {"a2", false, 0},
+ {"2a", false, 0},
+ {"2a2", false, 0},
+ {".0", false, 0},
+ {".1", false, 0},
+ {"-.2", false, 0},
+ {"+.3", false, 0},
+ {"1.23", false, 0},
+ {"-273.15", false, 0},
+ {"+98.6", false, 0},
+ {"1e1", false, 0},
+ {"1E1", false, 0},
+ {"0x123p4", false, 0},
+ {"infinity", false, 0},
+ {"NaN", false, 0},
+ {"-9223372036854775810", false, 0},
+ {"-9223372036854775809", false, 0},
+ {"9223372036854775808", false, 0},
+ {"9223372036854775809", false, 0},
+ {"18446744073709551615", false, 0},
+ {"18446744073709551616", false, 0},
+ };
+
+ for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ int value;
+ bool valid = StringToNumber(kTestData[index].string, &value);
+ if (kTestData[index].valid) {
+ EXPECT_TRUE(valid) << "index " << index << ", string "
+ << kTestData[index].string;
+ if (valid) {
+ EXPECT_EQ(kTestData[index].value, value)
+ << "index " << index << ", string " << kTestData[index].string;
+ }
+ } else {
+ EXPECT_FALSE(valid) << "index " << index << ", string "
+ << kTestData[index].string << ", value " << value;
+ }
+ }
+
+ // Ensure that embedded NUL characters are treated as bad input. The string
+ // is split to avoid MSVC warning:
+ // "decimal digit terminates octal escape sequence".
+ const char input[] = "6\000" "6";
+ base::StringPiece input_string(input, arraysize(input) - 1);
+ int output;
+ EXPECT_FALSE(StringToNumber(input_string, &output));
+
+ // Ensure that a NUL is not required at the end of the string.
+ EXPECT_TRUE(StringToNumber(base::StringPiece("66", 1), &output));
+ EXPECT_EQ(6, output);
+}
+
+TEST(StringNumberConversion, StringToUnsignedInt) {
+ const struct {
+ const char* string;
+ bool valid;
+ unsigned int value;
+ } kTestData[] = {
+ {"", false, 0},
+ {"0", true, 0},
+ {"1", true, 1},
+ {"2147483647", true, 2147483647},
+ {"2147483648", true, 2147483648},
+ {"4294967295", true, std::numeric_limits<unsigned int>::max()},
+ {"4294967296", false, 0},
+ {"-1", false, 0},
+ {"-2147483648", false, 0},
+ {"-2147483649", false, 0},
+ {"00", true, 0},
+ {"01", true, 1},
+ {"-01", false, 0},
+ {"+2", true, 2},
+ {"0x10", true, 16},
+ {"-0x10", false, 0},
+ {"+0x20", true, 32},
+ {"0xf", true, 15},
+ {"0xg", false, 0},
+ {"0x7fffffff", true, 0x7fffffff},
+ {"0x7FfFfFfF", true, 0x7fffffff},
+ {"0x80000000", true, 0x80000000},
+ {"0xFFFFFFFF", true, 0xffffffff},
+ {"-0x7fffffff", false, 0},
+ {"-0x80000000", false, 0},
+ {"-0x80000001", false, 0},
+ {"-0xffffffff", false, 0},
+ {"0x100000000", false, 0},
+ {"0xabcdef", true, 11259375},
+ {"010", true, 8},
+ {"-010", false, 0},
+ {"+020", true, 16},
+ {"07", true, 7},
+ {"08", false, 0},
+ {" 0", false, 0},
+ {"0 ", false, 0},
+ {" 0 ", false, 0},
+ {" 1", false, 0},
+ {"1 ", false, 0},
+ {" 1 ", false, 0},
+ {"a2", false, 0},
+ {"2a", false, 0},
+ {"2a2", false, 0},
+ {".0", false, 0},
+ {".1", false, 0},
+ {"-.2", false, 0},
+ {"+.3", false, 0},
+ {"1.23", false, 0},
+ {"-273.15", false, 0},
+ {"+98.6", false, 0},
+ {"1e1", false, 0},
+ {"1E1", false, 0},
+ {"0x123p4", false, 0},
+ {"infinity", false, 0},
+ {"NaN", false, 0},
+ {"-9223372036854775810", false, 0},
+ {"-9223372036854775809", false, 0},
+ {"9223372036854775808", false, 0},
+ {"9223372036854775809", false, 0},
+ {"18446744073709551615", false, 0},
+ {"18446744073709551616", false, 0},
+ };
+
+ for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ unsigned int value;
+ bool valid = StringToNumber(kTestData[index].string, &value);
+ if (kTestData[index].valid) {
+ EXPECT_TRUE(valid) << "index " << index << ", string "
+ << kTestData[index].string;
+ if (valid) {
+ EXPECT_EQ(kTestData[index].value, value)
+ << "index " << index << ", string " << kTestData[index].string;
+ }
+ } else {
+ EXPECT_FALSE(valid) << "index " << index << ", string "
+ << kTestData[index].string << ", value " << value;
+ }
+ }
+
+ // Ensure that embedded NUL characters are treated as bad input. The string
+ // is split to avoid MSVC warning:
+ // "decimal digit terminates octal escape sequence".
+ const char input[] = "6\000" "6";
+ base::StringPiece input_string(input, arraysize(input) - 1);
+ unsigned int output;
+ EXPECT_FALSE(StringToNumber(input_string, &output));
+
+ // Ensure that a NUL is not required at the end of the string.
+ EXPECT_TRUE(StringToNumber(base::StringPiece("66", 1), &output));
+ EXPECT_EQ(6u, output);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/stdlib/strlcpy.cc b/chromium/third_party/crashpad/crashpad/util/stdlib/strlcpy.cc
new file mode 100644
index 00000000000..90d6d96ecc7
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/stdlib/strlcpy.cc
@@ -0,0 +1,32 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/stdlib/strlcpy.h"
+
+namespace crashpad {
+
+size_t c16lcpy(base::char16* destination,
+ const base::char16* source,
+ size_t length) {
+ size_t source_length = base::c16len(source);
+ if (source_length < length) {
+ base::c16memcpy(destination, source, source_length + 1);
+ } else if (length != 0) {
+ base::c16memcpy(destination, source, length - 1);
+ destination[length - 1] = '\0';
+ }
+ return source_length;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/stdlib/strlcpy.h b/chromium/third_party/crashpad/crashpad/util/stdlib/strlcpy.h
new file mode 100644
index 00000000000..3fcc270f837
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/stdlib/strlcpy.h
@@ -0,0 +1,54 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_STDLIB_STRLCPY_H_
+#define CRASHPAD_UTIL_STDLIB_STRLCPY_H_
+
+#include <stdint.h>
+
+#include "base/strings/string16.h"
+
+namespace crashpad {
+
+//! \brief Copy a `NUL`-terminated char16-based string to a fixed-size buffer.
+//!
+//! This function behaves identically to `strlcpy()`, but it operates on char16
+//! data instead of `char` data. It copies the `NUL`-terminated string in the
+//! buffer beginning at \a source to the buffer of size \a length at \a
+//! destination, ensuring that the destination buffer is `NUL`-terminated. No
+//! data will be written outside of the \a destination buffer, but if \a length
+//! is smaller than the length of the string at \a source, the string will be
+//! truncated.
+//!
+//! \param[out] destination A pointer to a buffer of at least size \a length
+//! char16 units (not bytes). The string will be copied to this buffer,
+//! possibly with truncation, and `NUL`-terminated. Nothing will be written
+//! following the `NUL` terminator.
+//! \param[in] source A pointer to a `NUL`-terminated string of char16 data. The
+//! `NUL` terminator must be a `NUL` value in a char16 unit, not just a
+//! single `NUL` byte.
+//! \param[in] length The length of the \a destination buffer in char16 units,
+//! not bytes. A maximum of \a `length - 1` char16 units from \a source will
+//! be copied to \a destination.
+//!
+//! \return The length of the \a source string in char16 units, not including
+//! its `NUL` terminator. When truncation occurs, the return value will be
+//! equal to or greater than than the \a length parameter.
+size_t c16lcpy(base::char16* destination,
+ const base::char16* source,
+ size_t length);
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_STDLIB_STRLCPY_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/stdlib/strlcpy_test.cc b/chromium/third_party/crashpad/crashpad/util/stdlib/strlcpy_test.cc
new file mode 100644
index 00000000000..b9af201372a
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/stdlib/strlcpy_test.cc
@@ -0,0 +1,95 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/stdlib/strlcpy.h"
+
+#include <string.h>
+
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "base/format_macros.h"
+#include "base/strings/string16.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(strlcpy, c16lcpy) {
+ // Use a destination buffer that’s larger than the length passed to c16lcpy.
+ // The unused portion is a guard area that must not be written to.
+ struct TestBuffer {
+ base::char16 lead_guard[64];
+ base::char16 data[128];
+ base::char16 trail_guard[64];
+ };
+ TestBuffer expected_untouched;
+ memset(&expected_untouched, 0xa5, sizeof(expected_untouched));
+
+ // Test with M, é, Ā, ő, and Ḙ. This is a mix of characters that have zero and
+ // nonzero low and high bytes.
+ const base::char16 test_characters[] = {0x4d, 0xe9, 0x100, 0x151, 0x1e18};
+
+ for (size_t index = 0; index < arraysize(test_characters); ++index) {
+ base::char16 test_character = test_characters[index];
+ SCOPED_TRACE(base::StringPrintf(
+ "character index %" PRIuS ", character 0x%x", index, test_character));
+ for (size_t length = 0; length < 256; ++length) {
+ SCOPED_TRACE(
+ base::StringPrintf("index %" PRIuS, length));
+ base::string16 test_string(length, test_character);
+
+ TestBuffer destination;
+ memset(&destination, 0xa5, sizeof(destination));
+
+ EXPECT_EQ(length,
+ c16lcpy(destination.data,
+ test_string.c_str(),
+ arraysize(destination.data)));
+
+ // Make sure that the destination buffer is NUL-terminated, and that as
+ // much of the test string was copied as could fit.
+ size_t expected_destination_length =
+ std::min(length, arraysize(destination.data) - 1);
+
+ EXPECT_EQ('\0', destination.data[expected_destination_length]);
+ EXPECT_EQ(expected_destination_length, base::c16len(destination.data));
+ EXPECT_TRUE(base::c16memcmp(test_string.c_str(),
+ destination.data,
+ expected_destination_length) == 0);
+
+ // Make sure that the portion of the destination buffer that was not used
+ // was not touched. This includes the guard areas and the unused portion
+ // of the buffer passed to c16lcpy.
+ EXPECT_TRUE(base::c16memcmp(expected_untouched.lead_guard,
+ destination.lead_guard,
+ arraysize(destination.lead_guard)) == 0);
+ size_t expected_untouched_length =
+ arraysize(destination.data) - expected_destination_length - 1;
+ EXPECT_TRUE(
+ base::c16memcmp(expected_untouched.data,
+ &destination.data[expected_destination_length + 1],
+ expected_untouched_length) == 0);
+ EXPECT_TRUE(base::c16memcmp(expected_untouched.trail_guard,
+ destination.trail_guard,
+ arraysize(destination.trail_guard)) == 0);
+ }
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/stdlib/strnlen.cc b/chromium/third_party/crashpad/crashpad/util/stdlib/strnlen.cc
new file mode 100644
index 00000000000..3016bf63efa
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/stdlib/strnlen.cc
@@ -0,0 +1,47 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/stdlib/strnlen.h"
+
+#if defined(OS_MACOSX) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
+// Redeclare a method only available on OSX 10.7+ to suppress a
+// -Wpartial-availability warning.
+extern "C" {
+size_t strnlen(const char* string, size_t max_length);
+} // extern "C"
+#endif
+
+namespace crashpad {
+
+size_t strnlen(const char* string, size_t max_length) {
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
+ if (::strnlen) {
+ return ::strnlen(string, max_length);
+ }
+#endif
+
+ for (size_t index = 0; index < max_length; ++index) {
+ if (string[index] == '\0') {
+ return index;
+ }
+ }
+
+ return max_length;
+}
+
+} // namespace crashpad
+
+#endif
diff --git a/chromium/third_party/crashpad/crashpad/util/stdlib/strnlen.h b/chromium/third_party/crashpad/crashpad/util/stdlib/strnlen.h
new file mode 100644
index 00000000000..f1bbe63829a
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/stdlib/strnlen.h
@@ -0,0 +1,49 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_STDLIB_STRNLEN_H_
+#define CRASHPAD_UTIL_STDLIB_STRNLEN_H_
+
+#include <string.h>
+
+#include "build/build_config.h"
+
+#if defined(OS_MACOSX)
+#include <AvailabilityMacros.h>
+#endif
+
+namespace crashpad {
+
+//! \brief Returns the length of a string, not to exceed a maximum.
+//!
+//! \param[in] string The string whose length is to be calculated.
+//! \param[in] max_length The maximum length to return.
+//!
+//! \return The length of \a string, determined as the index of the first `NUL`
+//! byte found, not exceeding \a max_length.
+//!
+//! \note This function is provided because it was introduced in POSIX.1-2008,
+//! and not all systems’ standard libraries provide an implementation.
+size_t strnlen(const char* string, size_t max_length);
+
+#if !defined(OS_MACOSX) || \
+ MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7
+inline size_t strnlen(const char* string, size_t max_length) {
+ return ::strnlen(string, max_length);
+}
+#endif
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_STDLIB_STRNLEN_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/stdlib/strnlen_test.cc b/chromium/third_party/crashpad/crashpad/util/stdlib/strnlen_test.cc
new file mode 100644
index 00000000000..35ca7f1a6e8
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/stdlib/strnlen_test.cc
@@ -0,0 +1,45 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/stdlib/strnlen.h"
+
+#include <string.h>
+
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(strnlen, strnlen) {
+ const char kTestBuffer[] = "abc\0d";
+ ASSERT_EQ(3u, strlen(kTestBuffer));
+ EXPECT_EQ(0u, crashpad::strnlen(kTestBuffer, 0));
+ EXPECT_EQ(1u, crashpad::strnlen(kTestBuffer, 1));
+ EXPECT_EQ(2u, crashpad::strnlen(kTestBuffer, 2));
+ EXPECT_EQ(3u, crashpad::strnlen(kTestBuffer, 3));
+ EXPECT_EQ(3u, crashpad::strnlen(kTestBuffer, 4));
+ EXPECT_EQ(3u, crashpad::strnlen(kTestBuffer, 5));
+ EXPECT_EQ(3u, crashpad::strnlen(kTestBuffer, 6));
+
+ const char kEmptyBuffer[] = "\0";
+ ASSERT_EQ(0u, strlen(kEmptyBuffer));
+ EXPECT_EQ(0u, crashpad::strnlen(kEmptyBuffer, 0));
+ EXPECT_EQ(0u, crashpad::strnlen(kEmptyBuffer, 1));
+ EXPECT_EQ(0u, crashpad::strnlen(kEmptyBuffer, 2));
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/string/split_string.cc b/chromium/third_party/crashpad/crashpad/util/string/split_string.cc
new file mode 100644
index 00000000000..2123b4db057
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/string/split_string.cc
@@ -0,0 +1,33 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/string/split_string.h"
+
+namespace crashpad {
+
+bool SplitString(const std::string& string,
+ char delimiter,
+ std::string* left,
+ std::string* right) {
+ size_t delimiter_pos = string.find(delimiter);
+ if (delimiter_pos == 0 || delimiter_pos == std::string::npos) {
+ return false;
+ }
+
+ left->assign(string, 0, delimiter_pos);
+ right->assign(string, delimiter_pos + 1, std::string::npos);
+ return true;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/string/split_string.h b/chromium/third_party/crashpad/crashpad/util/string/split_string.h
new file mode 100644
index 00000000000..bb729041a86
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/string/split_string.h
@@ -0,0 +1,41 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_STRING_SPLIT_STRING_H_
+#define CRASHPAD_UTIL_STRING_SPLIT_STRING_H_
+
+#include <string>
+
+namespace crashpad {
+
+//! \brief Splits a string into two parts at the first delimiter found.
+//!
+//! \param[in] string The string to split.
+//! \param[in] delimiter The delimiter to split at.
+//! \param[out] left The portion of \a string up to, but not including, the
+//! first \a delimiter character.
+//! \param[out] right The portion of \a string after the first \a delimiter
+//! character.
+//!
+//! \return `true` if \a string was split successfully. `false` if \a string
+//! did not contain a \delimiter character or began with a \delimiter
+//! character.
+bool SplitString(const std::string& string,
+ char delimiter,
+ std::string* left,
+ std::string* right);
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_STRING_SPLIT_STRING_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/string/split_string_test.cc b/chromium/third_party/crashpad/crashpad/util/string/split_string_test.cc
new file mode 100644
index 00000000000..7d205319c62
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/string/split_string_test.cc
@@ -0,0 +1,63 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/string/split_string.h"
+
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(SplitString, SplitString) {
+ std::string left;
+ std::string right;
+
+ EXPECT_FALSE(SplitString("", '=', &left, &right));
+ EXPECT_FALSE(SplitString("no equals", '=', &left, &right));
+ EXPECT_FALSE(SplitString("=", '=', &left, &right));
+ EXPECT_FALSE(SplitString("=beginequals", '=', &left, &right));
+
+ ASSERT_TRUE(SplitString("a=b", '=', &left, &right));
+ EXPECT_EQ("a", left);
+ EXPECT_EQ("b", right);
+
+ ASSERT_TRUE(SplitString("EndsEquals=", '=', &left, &right));
+ EXPECT_EQ("EndsEquals", left);
+ EXPECT_TRUE(right.empty());
+
+ ASSERT_TRUE(SplitString("key=VALUE", '=', &left, &right));
+ EXPECT_EQ("key", left);
+ EXPECT_EQ("VALUE", right);
+
+ EXPECT_FALSE(SplitString("a=b", '|', &left, &right));
+
+ ASSERT_TRUE(SplitString("ls | less", '|', &left, &right));
+ EXPECT_EQ("ls ", left);
+ EXPECT_EQ(" less", right);
+
+ ASSERT_TRUE(SplitString("when in", ' ', &left, &right));
+ EXPECT_EQ("when", left);
+ EXPECT_EQ("in", right);
+
+ ASSERT_TRUE(SplitString("zoo", 'o', &left, &right));
+ EXPECT_EQ("z", left);
+ EXPECT_EQ("o", right);
+
+ ASSERT_FALSE(SplitString("ooze", 'o', &left, &right));
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore.h b/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore.h
new file mode 100644
index 00000000000..ac77cb7cbdf
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore.h
@@ -0,0 +1,80 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_SYNCHRONIZATION_SEMAPHORE_H_
+#define CRASHPAD_UTIL_SYNCHRONIZATION_SEMAPHORE_H_
+
+#include "build/build_config.h"
+
+#if defined(OS_MACOSX)
+#include <dispatch/dispatch.h>
+#elif defined(OS_WIN)
+#include <windows.h>
+#else
+#include <semaphore.h>
+#endif
+
+namespace crashpad {
+
+//! \brief An anonymous in-process counting sempahore.
+class Semaphore {
+ public:
+ //! \brief Initializes the semaphore.
+ //!
+ //! \param[in] value The initial value of the semaphore.
+ //!
+ //! If the semaphore cannot be created, execution is terminated.
+ explicit Semaphore(int value);
+
+ ~Semaphore();
+
+ //! \brief Performs the wait (or “procure”) operation on the semaphore.
+ //!
+ //! Atomically decrements the value of the semaphore by 1. If the new value is
+ //! negative, this function blocks and will not return until the semaphore’s
+ //! value is incremented to 0 by Signal().
+ //!
+ //! \sa TimedWait()
+ void Wait();
+
+ //! \brief Performs a timed wait (or “procure”) operation on the semaphore.
+ //!
+ //! \param[in] seconds The maximum number of seconds to wait for the operation
+ //! to complete.
+ //!
+ //! \return `false` if the wait timed out, `true` otherwise.
+ //!
+ //! This method is simlar to Wait(), except that the amount of time that it
+ //! blocks is limited.
+ bool TimedWait(double seconds);
+
+ //! \brief Performs the signal (or “post”) operation on the semaphore.
+ //!
+ //! Atomically increments the value of the semaphore by 1. If the new value is
+ //! 0, a caller blocked in Wait() will be awakened.
+ void Signal();
+
+ private:
+#if defined(OS_MACOSX)
+ dispatch_semaphore_t semaphore_;
+#elif defined(OS_WIN)
+ HANDLE semaphore_;
+#else
+ sem_t semaphore_;
+#endif
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_SYNCHRONIZATION_SEMAPHORE_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_mac.cc b/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_mac.cc
new file mode 100644
index 00000000000..e8a79ab4c2d
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_mac.cc
@@ -0,0 +1,45 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/synchronization/semaphore.h"
+
+#include "base/logging.h"
+
+namespace crashpad {
+
+Semaphore::Semaphore(int value)
+ : semaphore_(dispatch_semaphore_create(value)) {
+ CHECK(semaphore_) << "dispatch_semaphore_create";
+}
+
+Semaphore::~Semaphore() {
+ dispatch_release(semaphore_);
+}
+
+void Semaphore::Wait() {
+ CHECK_EQ(dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER), 0);
+}
+
+bool Semaphore::TimedWait(double seconds) {
+ DCHECK_GE(seconds, 0.0);
+ const dispatch_time_t timeout =
+ dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC);
+ return dispatch_semaphore_wait(semaphore_, timeout) == 0;
+}
+
+void Semaphore::Signal() {
+ dispatch_semaphore_signal(semaphore_);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_posix.cc b/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_posix.cc
new file mode 100644
index 00000000000..973f0a5d503
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_posix.cc
@@ -0,0 +1,57 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/synchronization/semaphore.h"
+
+#include <errno.h>
+
+#include <cmath>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+namespace crashpad {
+
+#if !defined(OS_MACOSX)
+
+Semaphore::Semaphore(int value) {
+ PCHECK(sem_init(&semaphore_, 0, value) == 0) << "sem_init";
+}
+
+Semaphore::~Semaphore() {
+ PCHECK(sem_destroy(&semaphore_) == 0) << "sem_destroy";
+}
+
+void Semaphore::Wait() {
+ PCHECK(HANDLE_EINTR(sem_wait(&semaphore_)) == 0) << "sem_wait";
+}
+
+bool Semaphore::TimedWait(double seconds) {
+ DCHECK_GE(seconds, 0.0);
+ timespec timeout;
+ timeout.tv_sec = seconds;
+ timeout.tv_nsec = (seconds - trunc(seconds)) * 1E9;
+
+ int rv = HANDLE_EINTR(sem_timedwait(&semaphore_, &timeout));
+ PCHECK(rv == 0 || errno == ETIMEDOUT) << "sem_timedwait";
+ return rv == 0;
+}
+
+void Semaphore::Signal() {
+ PCHECK(sem_post(&semaphore_) == 0) << "sem_post";
+}
+
+#endif
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_test.cc b/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_test.cc
new file mode 100644
index 00000000000..f359cbddcee
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_test.cc
@@ -0,0 +1,131 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/synchronization/semaphore.h"
+
+#if defined(OS_POSIX)
+#include <pthread.h>
+#endif // OS_POSIX
+
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(Semaphore, Simple) {
+ Semaphore semaphore(1);
+ semaphore.Wait();
+ semaphore.Signal();
+}
+
+TEST(Semaphore, TimedWait) {
+ Semaphore semaphore(0);
+ semaphore.Signal();
+ EXPECT_TRUE(semaphore.TimedWait(0.01)); // 10ms
+}
+
+TEST(Semaphore, TimedWaitTimeout) {
+ Semaphore semaphore(0);
+ EXPECT_FALSE(semaphore.TimedWait(0.01)); // 10ms
+}
+
+struct ThreadMainInfo {
+#if defined(OS_POSIX)
+ pthread_t pthread;
+#elif defined(OS_WIN)
+ HANDLE thread;
+#endif
+ Semaphore* semaphore;
+ size_t iterations;
+};
+
+#if defined(OS_POSIX)
+void*
+#elif defined(OS_WIN)
+DWORD WINAPI
+#endif // OS_POSIX
+ThreadMain(void* argument) {
+ ThreadMainInfo* info = reinterpret_cast<ThreadMainInfo*>(argument);
+ for (size_t iteration = 0; iteration < info->iterations; ++iteration) {
+ info->semaphore->Wait();
+ }
+#if defined(OS_POSIX)
+ return nullptr;
+#elif defined(OS_WIN)
+ return 0;
+#endif // OS_POSIX
+}
+
+void StartThread(ThreadMainInfo* info) {
+#if defined(OS_POSIX)
+ int rv = pthread_create(&info->pthread, nullptr, ThreadMain, info);
+ ASSERT_EQ(0, rv) << "pthread_create";
+#elif defined(OS_WIN)
+ info->thread = CreateThread(nullptr, 0, ThreadMain, info, 0, nullptr);
+ ASSERT_NE(nullptr, info->thread) << "CreateThread";
+#endif // OS_POSIX
+}
+
+void JoinThread(ThreadMainInfo* info) {
+#if defined(OS_POSIX)
+ int rv = pthread_join(info->pthread, nullptr);
+ EXPECT_EQ(0, rv) << "pthread_join";
+#elif defined(OS_WIN)
+ DWORD result = WaitForSingleObject(info->thread, INFINITE);
+ EXPECT_EQ(WAIT_OBJECT_0, result) << "WaitForSingleObject";
+#endif // OS_POSIX
+}
+
+TEST(Semaphore, Threaded) {
+ Semaphore semaphore(0);
+ ThreadMainInfo info;
+ info.semaphore = &semaphore;
+ info.iterations = 1;
+
+ ASSERT_NO_FATAL_FAILURE(StartThread(&info));
+
+ semaphore.Signal();
+
+ JoinThread(&info);
+}
+
+TEST(Semaphore, TenThreaded) {
+ // This test has a smaller initial value (5) than threads contending for these
+ // resources (10), and the threads each try to obtain the resource a different
+ // number of times.
+ Semaphore semaphore(5);
+ const size_t kThreads = 10;
+ ThreadMainInfo info[kThreads];
+ size_t iterations = 0;
+ for (size_t index = 0; index < kThreads; ++index) {
+ info[index].semaphore = &semaphore;
+ info[index].iterations = index;
+ iterations += info[index].iterations;
+
+ ASSERT_NO_FATAL_FAILURE(StartThread(&info[index]));
+ }
+
+ for (size_t iteration = 0; iteration < iterations; ++iteration) {
+ semaphore.Signal();
+ }
+
+ for (size_t index = 0; index < kThreads; ++index) {
+ JoinThread(&info[index]);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_win.cc b/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_win.cc
new file mode 100644
index 00000000000..962c7baeac6
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_win.cc
@@ -0,0 +1,50 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/synchronization/semaphore.h"
+
+#include <limits>
+
+#include "base/logging.h"
+
+namespace crashpad {
+
+Semaphore::Semaphore(int value)
+ : semaphore_(CreateSemaphore(nullptr,
+ value,
+ std::numeric_limits<LONG>::max(),
+ nullptr)) {
+ PCHECK(semaphore_) << "CreateSemaphore";
+}
+
+Semaphore::~Semaphore() {
+ PCHECK(CloseHandle(semaphore_));
+}
+
+void Semaphore::Wait() {
+ PCHECK(WaitForSingleObject(semaphore_, INFINITE) == WAIT_OBJECT_0);
+}
+
+bool Semaphore::TimedWait(double seconds) {
+ DCHECK_GE(seconds, 0.0);
+ DWORD rv = WaitForSingleObject(semaphore_, static_cast<DWORD>(seconds * 1E3));
+ PCHECK(rv == WAIT_OBJECT_0 || rv == WAIT_TIMEOUT) << "WaitForSingleObject";
+ return rv == WAIT_OBJECT_0;
+}
+
+void Semaphore::Signal() {
+ PCHECK(ReleaseSemaphore(semaphore_, 1, nullptr));
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/thread/thread_log_messages.cc b/chromium/third_party/crashpad/crashpad/util/thread/thread_log_messages.cc
new file mode 100644
index 00000000000..bada15a2b7c
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/thread/thread_log_messages.cc
@@ -0,0 +1,99 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/thread/thread_log_messages.h"
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/thread_local_storage.h"
+
+namespace crashpad {
+
+namespace {
+
+// While an object of this class exists, it will be set as the log message
+// handler. A thread may register its thread-specific log message list to
+// receive messages produced just on that thread.
+//
+// Only one object of this class may exist in the program at a time. There must
+// not be any log message handler in effect when it is created, and nothing else
+// can be set as a log message handler while an object of this class exists.
+//
+// Practically, the only object of this class that might exist is managed by the
+// g_master lazy instance, which will create it upon first use.
+class ThreadLogMessagesMaster {
+ public:
+ ThreadLogMessagesMaster() {
+ DCHECK(!tls_.initialized());
+ tls_.Initialize(nullptr);
+ DCHECK(tls_.initialized());
+
+ DCHECK(!logging::GetLogMessageHandler());
+ logging::SetLogMessageHandler(LogMessageHandler);
+ }
+
+ ~ThreadLogMessagesMaster() {
+ DCHECK_EQ(logging::GetLogMessageHandler(), LogMessageHandler);
+ logging::SetLogMessageHandler(nullptr);
+
+ tls_.Free();
+ }
+
+ void SetThreadMessageList(std::vector<std::string>* message_list) {
+ DCHECK_EQ(logging::GetLogMessageHandler(), LogMessageHandler);
+ DCHECK_NE(tls_.Get() != nullptr, message_list != nullptr);
+ tls_.Set(message_list);
+ }
+
+ private:
+ static bool LogMessageHandler(logging::LogSeverity severity,
+ const char* file_path,
+ int line,
+ size_t message_start,
+ const std::string& string) {
+ std::vector<std::string>* log_messages =
+ reinterpret_cast<std::vector<std::string>*>(tls_.Get());
+ if (log_messages) {
+ log_messages->push_back(string);
+ }
+
+ // Don’t consume the message. Allow it to be logged as if nothing was set as
+ // the log message handler.
+ return false;
+ }
+
+ static base::ThreadLocalStorage::StaticSlot tls_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadLogMessagesMaster);
+};
+
+// static
+base::ThreadLocalStorage::StaticSlot ThreadLogMessagesMaster::tls_
+ = TLS_INITIALIZER;
+
+base::LazyInstance<ThreadLogMessagesMaster>::Leaky g_master =
+ LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+ThreadLogMessages::ThreadLogMessages()
+ : log_messages_() {
+ g_master.Get().SetThreadMessageList(&log_messages_);
+}
+
+ThreadLogMessages::~ThreadLogMessages() {
+ g_master.Get().SetThreadMessageList(nullptr);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/thread/thread_log_messages.h b/chromium/third_party/crashpad/crashpad/util/thread/thread_log_messages.h
new file mode 100644
index 00000000000..3f850df94c5
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/thread/thread_log_messages.h
@@ -0,0 +1,48 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_THREAD_THREAD_LOG_MESSAGES_H_
+#define CRASHPAD_UTIL_THREAD_THREAD_LOG_MESSAGES_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+
+namespace crashpad {
+
+//! \brief Captures log messages produced on the current thread during an
+//! object’s lifetime.
+//!
+//! At most one object of this class type may exist on a single thread at a
+//! time. When using this class, no other part of the program may call
+//! `logging::SetLogMessageHandler()` at any time.
+class ThreadLogMessages {
+ public:
+ ThreadLogMessages();
+ ~ThreadLogMessages();
+
+ //! \return The log messages collected on the thread that this object was
+ //! created on since the time it was created.
+ const std::vector<std::string>& log_messages() const { return log_messages_; }
+
+ private:
+ std::vector<std::string> log_messages_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadLogMessages);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_THREAD_THREAD_LOG_MESSAGES_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/thread/thread_log_messages_test.cc b/chromium/third_party/crashpad/crashpad/util/thread/thread_log_messages_test.cc
new file mode 100644
index 00000000000..96e2e91a163
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/thread/thread_log_messages_test.cc
@@ -0,0 +1,190 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/thread/thread_log_messages.h"
+
+#include <string.h>
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+#include "test/thread.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(ThreadLogMessages, Empty) {
+ ThreadLogMessages thread_log_messages;
+
+ const std::vector<std::string>& log_messages =
+ thread_log_messages.log_messages();
+
+ EXPECT_TRUE(log_messages.empty());
+}
+
+// For a message formatted like "[preamble] message\n", returns just "message".
+// If the message is not formatted as expected, a gtest expectation failure will
+// be recorded and this function will return an empty string.
+std::string MessageString(const std::string& log_message) {
+ if (log_message.size() < 1) {
+ EXPECT_GE(log_message.size(), 1u);
+ return std::string();
+ }
+
+ const char kStartChar = '[';
+ if (log_message[0] != kStartChar) {
+ EXPECT_EQ(kStartChar, log_message[0]);
+ return std::string();
+ }
+
+ const char kFindString[] = "] ";
+ size_t pos = log_message.find(kFindString);
+ if (pos == std::string::npos) {
+ EXPECT_NE(std::string::npos, pos);
+ return std::string();
+ }
+
+ std::string message_string = log_message.substr(pos + strlen(kFindString));
+ if (message_string.size() < 1) {
+ EXPECT_GE(message_string.size(), 1u);
+ return std::string();
+ }
+
+ const char kEndChar = '\n';
+ if (message_string[message_string.size() - 1] != kEndChar) {
+ EXPECT_NE(message_string[message_string.size() - 1], kEndChar);
+ return std::string();
+ }
+
+ message_string.resize(message_string.size() - 1);
+ return message_string;
+}
+
+TEST(ThreadLogMessages, Basic) {
+ // Logging must be enabled at least at this level for this test to work.
+ ASSERT_TRUE(LOG_IS_ON(INFO));
+
+ {
+ const char* const kMessages[] = {
+ "An info message",
+ "A warning message",
+ "An error message",
+ };
+
+ ThreadLogMessages thread_log_messages;
+
+ LOG(INFO) << kMessages[0];
+ LOG(WARNING) << kMessages[1];
+ LOG(ERROR) << kMessages[2];
+
+ const std::vector<std::string>& log_messages =
+ thread_log_messages.log_messages();
+
+ EXPECT_EQ(arraysize(kMessages), log_messages.size());
+ for (size_t index = 0; index < arraysize(kMessages); ++index) {
+ EXPECT_EQ(kMessages[index], MessageString(log_messages[index]))
+ << "index " << index;
+ }
+ }
+
+ {
+ const char kMessage[] = "Sample error message";
+
+ ThreadLogMessages thread_log_messages;
+
+ LOG(ERROR) << kMessage;
+
+ const std::vector<std::string>& log_messages =
+ thread_log_messages.log_messages();
+
+ EXPECT_EQ(1u, log_messages.size());
+ EXPECT_EQ(kMessage, MessageString(log_messages[0]));
+ }
+
+ {
+ ThreadLogMessages thread_log_messages;
+
+ LOG(INFO) << "I can't believe I " << "streamed" << " the whole thing.";
+
+ const std::vector<std::string>& log_messages =
+ thread_log_messages.log_messages();
+
+ EXPECT_EQ(1u, log_messages.size());
+ EXPECT_EQ("I can't believe I streamed the whole thing.",
+ MessageString(log_messages[0]));
+ }
+}
+
+class LoggingTestThread : public Thread {
+ public:
+ LoggingTestThread() : thread_number_(0), start_(0), count_(0) {}
+ ~LoggingTestThread() override {}
+
+ void Initialize(size_t thread_number, int start, int count) {
+ thread_number_ = thread_number;
+ start_ = start;
+ count_ = count;
+ }
+
+ private:
+ void ThreadMain() override {
+ ThreadLogMessages thread_log_messages;
+
+ std::vector<std::string> expected_messages;
+ for (int index = start_; index < start_ + count_; ++index) {
+ std::string message = base::StringPrintf("message %d", index);
+ expected_messages.push_back(message);
+ LOG(WARNING) << message;
+ }
+
+ const std::vector<std::string>& log_messages =
+ thread_log_messages.log_messages();
+
+ ASSERT_EQ(static_cast<size_t>(count_), log_messages.size());
+ for (size_t index = 0; index < log_messages.size(); ++index) {
+ EXPECT_EQ(expected_messages[index], MessageString(log_messages[index]))
+ << "thread_number_ " << thread_number_ << ", index " << index;
+ }
+ }
+
+ size_t thread_number_;
+ int start_;
+ int count_;
+
+ DISALLOW_COPY_AND_ASSIGN(LoggingTestThread);
+};
+
+TEST(ThreadLogMessages, Multithreaded) {
+ // Logging must be enabled at least at this level for this test to work.
+ ASSERT_TRUE(LOG_IS_ON(WARNING));
+
+ LoggingTestThread threads[20];
+ int start = 0;
+ for (size_t index = 0; index < arraysize(threads); ++index) {
+ threads[index].Initialize(
+ index, static_cast<int>(start), static_cast<int>(index));
+ start += static_cast<int>(index);
+
+ ASSERT_NO_FATAL_FAILURE(threads[index].Start());
+ }
+
+ for (LoggingTestThread& thread : threads) {
+ thread.Join();
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/util.gyp b/chromium/third_party/crashpad/crashpad/util/util.gyp
new file mode 100644
index 00000000000..e1bb129994c
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/util.gyp
@@ -0,0 +1,227 @@
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'includes': [
+ '../build/crashpad.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'crashpad_util',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../compat/compat.gyp:crashpad_compat',
+ '../third_party/mini_chromium/mini_chromium.gyp:base',
+ ],
+ 'include_dirs': [
+ '..',
+ '<(INTERMEDIATE_DIR)',
+ ],
+ 'sources': [
+ 'file/file_io.cc',
+ 'file/file_io.h',
+ 'file/file_io_posix.cc',
+ 'file/file_io_win.cc',
+ 'file/file_reader.cc',
+ 'file/file_reader.h',
+ 'file/file_seeker.cc',
+ 'file/file_seeker.h',
+ 'file/file_writer.cc',
+ 'file/file_writer.h',
+ 'file/string_file.cc',
+ 'file/string_file.h',
+ 'mac/checked_mach_address_range.h',
+ 'mac/launchd.h',
+ 'mac/launchd.mm',
+ 'mac/mac_util.cc',
+ 'mac/mac_util.h',
+ 'mac/service_management.cc',
+ 'mac/service_management.h',
+ 'mac/xattr.cc',
+ 'mac/xattr.h',
+ 'mach/child_port.defs',
+ 'mach/child_port_handshake.cc',
+ 'mach/child_port_handshake.h',
+ 'mach/child_port_server.cc',
+ 'mach/child_port_server.h',
+ 'mach/child_port_types.h',
+ 'mach/composite_mach_message_server.cc',
+ 'mach/composite_mach_message_server.h',
+ 'mach/exc_client_variants.cc',
+ 'mach/exc_client_variants.h',
+ 'mach/exc_server_variants.cc',
+ 'mach/exc_server_variants.h',
+ 'mach/exception_behaviors.cc',
+ 'mach/exception_behaviors.h',
+ 'mach/exception_ports.cc',
+ 'mach/exception_ports.h',
+ 'mach/exception_types.cc',
+ 'mach/exception_types.h',
+ 'mach/mach_extensions.cc',
+ 'mach/mach_extensions.h',
+ 'mach/mach_message.cc',
+ 'mach/mach_message.h',
+ 'mach/mach_message_server.cc',
+ 'mach/mach_message_server.h',
+ 'mach/notify_server.cc',
+ 'mach/notify_server.h',
+ 'mach/scoped_task_suspend.cc',
+ 'mach/scoped_task_suspend.h',
+ 'mach/symbolic_constants_mach.cc',
+ 'mach/symbolic_constants_mach.h',
+ 'mach/task_for_pid.cc',
+ 'mach/task_for_pid.h',
+ 'mach/task_memory.cc',
+ 'mach/task_memory.h',
+ 'misc/clock.h',
+ 'misc/clock_mac.cc',
+ 'misc/clock_posix.cc',
+ 'misc/clock_win.cc',
+ 'misc/initialization_state.h',
+ 'misc/initialization_state_dcheck.cc',
+ 'misc/initialization_state_dcheck.h',
+ 'misc/scoped_forbid_return.cc',
+ 'misc/scoped_forbid_return.h',
+ 'misc/symbolic_constants_common.h',
+ 'misc/tri_state.h',
+ 'misc/uuid.cc',
+ 'misc/uuid.h',
+ 'net/http_body.cc',
+ 'net/http_body.h',
+ 'net/http_headers.cc',
+ 'net/http_headers.h',
+ 'net/http_multipart_builder.cc',
+ 'net/http_multipart_builder.h',
+ 'net/http_transport.cc',
+ 'net/http_transport.h',
+ 'net/http_transport_mac.mm',
+ 'net/http_transport_win.cc',
+ 'numeric/checked_address_range.cc',
+ 'numeric/checked_address_range.h',
+ 'numeric/checked_range.h',
+ 'numeric/in_range_cast.h',
+ 'numeric/int128.h',
+ 'numeric/safe_assignment.h',
+ 'posix/close_multiple.cc',
+ 'posix/close_multiple.h',
+ 'posix/close_stdio.cc',
+ 'posix/close_stdio.h',
+ 'posix/drop_privileges.cc',
+ 'posix/drop_privileges.h',
+ 'posix/process_info.h',
+ 'posix/process_info_mac.cc',
+ 'posix/symbolic_constants_posix.cc',
+ 'posix/symbolic_constants_posix.h',
+ 'stdlib/cxx.h',
+ 'stdlib/map_insert.h',
+ 'stdlib/objc.h',
+ 'stdlib/pointer_container.h',
+ 'stdlib/string_number_conversion.cc',
+ 'stdlib/string_number_conversion.h',
+ 'stdlib/strlcpy.cc',
+ 'stdlib/strlcpy.h',
+ 'stdlib/strnlen.cc',
+ 'stdlib/strnlen.h',
+ 'string/split_string.cc',
+ 'string/split_string.h',
+ 'synchronization/semaphore_mac.cc',
+ 'synchronization/semaphore_posix.cc',
+ 'synchronization/semaphore_win.cc',
+ 'synchronization/semaphore.h',
+ 'thread/thread_log_messages.cc',
+ 'thread/thread_log_messages.h',
+ 'win/address_types.h',
+ 'win/checked_win_address_range.h',
+ 'win/process_info.cc',
+ 'win/process_info.h',
+ 'win/process_structs.h',
+ 'win/scoped_handle.cc',
+ 'win/scoped_handle.h',
+ 'win/time.cc',
+ 'win/time.h',
+ ],
+ 'conditions': [
+ ['OS=="mac"', {
+ 'conditions': [
+ ['GENERATOR=="ninja"', {
+ # ninja’s rules can’t deal with sources that have paths relative
+ # to environment variables like SDKROOT. Copy the .defs files out
+ # of SDKROOT and into a place they can be referenced without any
+ # environment variables.
+ 'copies': [
+ {
+ 'destination': '<(INTERMEDIATE_DIR)/util/mach',
+ 'files': [
+ '$(SDKROOT)/usr/include/mach/exc.defs',
+ '$(SDKROOT)/usr/include/mach/mach_exc.defs',
+ '$(SDKROOT)/usr/include/mach/notify.defs',
+ ],
+ },
+ ],
+ 'sources': [
+ '<(INTERMEDIATE_DIR)/util/mach/exc.defs',
+ '<(INTERMEDIATE_DIR)/util/mach/mach_exc.defs',
+ '<(INTERMEDIATE_DIR)/util/mach/notify.defs',
+ ],
+ }, { # else: GENERATOR!="ninja"
+ # The Xcode generator does copies after rules, so the above trick
+ # won’t work, but its rules tolerate sources with SDKROOT-relative
+ # paths.
+ 'sources': [
+ '$(SDKROOT)/usr/include/mach/exc.defs',
+ '$(SDKROOT)/usr/include/mach/mach_exc.defs',
+ '$(SDKROOT)/usr/include/mach/notify.defs',
+ ],
+ }],
+ ],
+ 'rules': [
+ {
+ 'rule_name': 'mig',
+ 'extension': 'defs',
+ 'inputs': [
+ 'mach/mig.py',
+ ],
+ 'outputs': [
+ '<(INTERMEDIATE_DIR)/util/mach/<(RULE_INPUT_ROOT)User.c',
+ '<(INTERMEDIATE_DIR)/util/mach/<(RULE_INPUT_ROOT)Server.c',
+ '<(INTERMEDIATE_DIR)/util/mach/<(RULE_INPUT_ROOT).h',
+ '<(INTERMEDIATE_DIR)/util/mach/<(RULE_INPUT_ROOT)Server.h',
+ ],
+ 'action': [
+ 'python', '<@(_inputs)', '<(RULE_INPUT_PATH)', '<@(_outputs)'
+ ],
+ 'process_outputs_as_sources': 1,
+ },
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/CoreFoundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/IOKit.framework',
+ '$(SDKROOT)/usr/lib/libbsm.dylib',
+ ],
+ },
+ }],
+ ['OS=="win"', {
+ 'link_settings': {
+ 'libraries': [
+ '-lrpcrt4.lib',
+ '-lwinhttp.lib',
+ ],
+ },
+ }],
+ ],
+ },
+ ],
+}
diff --git a/chromium/third_party/crashpad/crashpad/util/util_test.gyp b/chromium/third_party/crashpad/crashpad/util/util_test.gyp
new file mode 100644
index 00000000000..82a1c3b3d0d
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/util_test.gyp
@@ -0,0 +1,152 @@
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'includes': [
+ '../build/crashpad.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'crashpad_util_test',
+ 'type': 'executable',
+ 'dependencies': [
+ 'util.gyp:crashpad_util',
+ '../compat/compat.gyp:crashpad_compat',
+ '../test/test.gyp:crashpad_test',
+ '../third_party/gmock/gmock.gyp:gmock',
+ '../third_party/gmock/gmock.gyp:gmock_main',
+ '../third_party/gtest/gtest.gyp:gtest',
+ '../third_party/mini_chromium/mini_chromium.gyp:base',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'file/file_io_test.cc',
+ 'file/string_file_test.cc',
+ 'mac/launchd_test.mm',
+ 'mac/mac_util_test.mm',
+ 'mac/service_management_test.mm',
+ 'mac/xattr_test.cc',
+ 'mach/child_port_handshake_test.cc',
+ 'mach/child_port_server_test.cc',
+ 'mach/composite_mach_message_server_test.cc',
+ 'mach/exc_client_variants_test.cc',
+ 'mach/exc_server_variants_test.cc',
+ 'mach/exception_behaviors_test.cc',
+ 'mach/exception_ports_test.cc',
+ 'mach/exception_types_test.cc',
+ 'mach/mach_extensions_test.cc',
+ 'mach/mach_message_server_test.cc',
+ 'mach/mach_message_test.cc',
+ 'mach/notify_server_test.cc',
+ 'mach/scoped_task_suspend_test.cc',
+ 'mach/symbolic_constants_mach_test.cc',
+ 'mach/task_memory_test.cc',
+ 'misc/clock_test.cc',
+ 'misc/initialization_state_dcheck_test.cc',
+ 'misc/initialization_state_test.cc',
+ 'misc/scoped_forbid_return_test.cc',
+ 'misc/uuid_test.cc',
+ 'net/http_body_test.cc',
+ 'net/http_body_test_util.cc',
+ 'net/http_body_test_util.h',
+ 'net/http_multipart_builder_test.cc',
+ 'net/http_transport_test.cc',
+ 'numeric/checked_address_range_test.cc',
+ 'numeric/checked_range_test.cc',
+ 'numeric/in_range_cast_test.cc',
+ 'numeric/int128_test.cc',
+ 'posix/process_info_test.cc',
+ 'posix/symbolic_constants_posix_test.cc',
+ 'stdlib/map_insert_test.cc',
+ 'stdlib/string_number_conversion_test.cc',
+ 'stdlib/strlcpy_test.cc',
+ 'stdlib/strnlen_test.cc',
+ 'string/split_string_test.cc',
+ 'synchronization/semaphore_test.cc',
+ 'thread/thread_log_messages_test.cc',
+ 'win/process_info_test.cc',
+ 'win/time_test.cc',
+ ],
+ 'conditions': [
+ ['OS=="mac"', {
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ ],
+ },
+ }],
+ ['OS=="win"', {
+ 'dependencies': [
+ 'crashpad_util_test_process_info_test_child_x64',
+ 'crashpad_util_test_process_info_test_child_x86',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '-lrpcrt4.lib',
+ ],
+ },
+ }],
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS=="win"', {
+ 'targets': [
+ {
+ 'target_name': 'crashpad_util_test_process_info_test_child_x64',
+ 'type': 'executable',
+ 'sources': [
+ 'win/process_info_test_child.cc',
+ ],
+ 'msvs_configuration_platform': 'x64',
+ # Set an unusually high load address to make sure that the main
+ # executable still appears as the first element in
+ # ProcessInfo::Modules().
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'AdditionalOptions': [
+ '/BASE:0x78000000',
+ '/FIXED',
+ ],
+ 'TargetMachine': '17', # x64.
+ },
+ },
+ },
+ {
+ # Same as above, but explicitly x86 to test 64->32 access.
+ 'target_name': 'crashpad_util_test_process_info_test_child_x86',
+ 'type': 'executable',
+ 'sources': [
+ 'win/process_info_test_child.cc',
+ ],
+ 'msvs_configuration_platform': 'x86',
+ # Set an unusually high load address to make sure that the main
+ # executable still appears as the first element in
+ # ProcessInfo::Modules().
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'AdditionalOptions': [
+ '/BASE:0x78000000',
+ '/FIXED',
+ ],
+ 'TargetMachine': '1', # x86.
+ },
+ },
+ },
+ ]
+ }],
+ ],
+}
diff --git a/chromium/third_party/crashpad/crashpad/util/win/address_types.h b/chromium/third_party/crashpad/crashpad/util/win/address_types.h
new file mode 100644
index 00000000000..f26a57eff56
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/win/address_types.h
@@ -0,0 +1,32 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_ADDRESS_TYPES_H_
+#define CRASHPAD_UTIL_WIN_ADDRESS_TYPES_H_
+
+#include <stdint.h>
+
+namespace crashpad {
+
+//! \brief Type used to represent an address in a process, potentially across
+//! bitness.
+using WinVMAddress = uint64_t;
+
+//! \brief Type used to represent the size of a memory range (with a
+//! WinVMAddress), potentially across bitness.
+using WinVMSize = uint64_t;
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_WIN_ADDRESS_TYPES_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/win/checked_win_address_range.h b/chromium/third_party/crashpad/crashpad/util/win/checked_win_address_range.h
new file mode 100644
index 00000000000..e86c7b7b0a8
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/win/checked_win_address_range.h
@@ -0,0 +1,36 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_CHECKED_WIN_ADDRESS_RANGE_H_
+#define CRASHPAD_UTIL_WIN_CHECKED_WIN_ADDRESS_RANGE_H_
+
+#include "util/numeric/checked_address_range.h"
+#include "util/win/address_types.h"
+
+namespace crashpad {
+
+//! \brief Ensures that a range, composed of a base and a size, does not
+//! overflow the pointer type of the process it describes a range in.
+//!
+//! This class checks bases of type WinVMAddress and sizes of type WinVMSize
+//! against a process whose pointer type is either 32 or 64 bits wide.
+//!
+//! Aside from varying the overall range on the basis of a process' pointer type
+//! width, this class functions very similarly to CheckedRange.
+using CheckedWinAddressRange =
+ internal::CheckedAddressRangeGeneric<WinVMAddress, WinVMSize>;
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_WIN_CHECKED_WIN_ADDRESS_RANGE_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/win/process_info.cc b/chromium/third_party/crashpad/crashpad/util/win/process_info.cc
new file mode 100644
index 00000000000..ca8277ed030
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/win/process_info.cc
@@ -0,0 +1,339 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/process_info.h"
+
+#include <winternl.h>
+
+#include "base/logging.h"
+#include "util/numeric/safe_assignment.h"
+#include "util/win/process_structs.h"
+
+namespace crashpad {
+
+namespace {
+
+NTSTATUS NtQueryInformationProcess(HANDLE process_handle,
+ PROCESSINFOCLASS process_information_class,
+ PVOID process_information,
+ ULONG process_information_length,
+ PULONG return_length) {
+ static decltype(::NtQueryInformationProcess)* nt_query_information_process =
+ reinterpret_cast<decltype(::NtQueryInformationProcess)*>(GetProcAddress(
+ LoadLibrary(L"ntdll.dll"), "NtQueryInformationProcess"));
+ DCHECK(nt_query_information_process);
+ return nt_query_information_process(process_handle,
+ process_information_class,
+ process_information,
+ process_information_length,
+ return_length);
+}
+
+bool IsProcessWow64(HANDLE process_handle) {
+ static decltype(IsWow64Process)* is_wow64_process =
+ reinterpret_cast<decltype(IsWow64Process)*>(
+ GetProcAddress(LoadLibrary(L"kernel32.dll"), "IsWow64Process"));
+ if (!is_wow64_process)
+ return false;
+ BOOL is_wow64;
+ if (!is_wow64_process(process_handle, &is_wow64)) {
+ PLOG(ERROR) << "IsWow64Process";
+ return false;
+ }
+ return is_wow64;
+}
+
+template <class T>
+bool ReadUnicodeString(HANDLE process,
+ const process_types::UNICODE_STRING<T>& us,
+ std::wstring* result) {
+ if (us.Length == 0) {
+ result->clear();
+ return true;
+ }
+ DCHECK_EQ(us.Length % sizeof(wchar_t), 0u);
+ result->resize(us.Length / sizeof(wchar_t));
+ SIZE_T bytes_read;
+ if (!ReadProcessMemory(process,
+ reinterpret_cast<const void*>(us.Buffer),
+ &result->operator[](0),
+ us.Length,
+ &bytes_read)) {
+ PLOG(ERROR) << "ReadProcessMemory UNICODE_STRING";
+ return false;
+ }
+ if (bytes_read != us.Length) {
+ LOG(ERROR) << "ReadProcessMemory UNICODE_STRING incorrect size";
+ return false;
+ }
+ return true;
+}
+
+template <class T> bool ReadStruct(HANDLE process, WinVMAddress at, T* into) {
+ SIZE_T bytes_read;
+ if (!ReadProcessMemory(process,
+ reinterpret_cast<const void*>(at),
+ into,
+ sizeof(T),
+ &bytes_read)) {
+ // We don't have a name for the type we're reading, so include the signature
+ // to get the type of T.
+ PLOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__;
+ return false;
+ }
+ if (bytes_read != sizeof(T)) {
+ LOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__ << " incorrect size";
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+template <class Traits>
+bool ReadProcessData(HANDLE process,
+ WinVMAddress peb_address_vmaddr,
+ ProcessInfo* process_info) {
+ Traits::Pointer peb_address;
+ if (!AssignIfInRange(&peb_address, peb_address_vmaddr)) {
+ LOG(ERROR) << "peb_address_vmaddr " << peb_address_vmaddr
+ << " out of range";
+ return false;
+ }
+
+ // Try to read the process environment block.
+ process_types::PEB<Traits> peb;
+ if (!ReadStruct(process, peb_address, &peb))
+ return false;
+
+ process_types::RTL_USER_PROCESS_PARAMETERS<Traits> process_parameters;
+ if (!ReadStruct(process, peb.ProcessParameters, &process_parameters))
+ return false;
+
+ if (!ReadUnicodeString(process,
+ process_parameters.CommandLine,
+ &process_info->command_line_)) {
+ return false;
+ }
+
+ process_types::PEB_LDR_DATA<Traits> peb_ldr_data;
+ if (!ReadStruct(process, peb.Ldr, &peb_ldr_data))
+ return false;
+
+ process_types::LDR_DATA_TABLE_ENTRY<Traits> ldr_data_table_entry;
+
+ // Include the first module in the memory order list to get our the main
+ // executable's name, as it's not included in initialization order below.
+ if (!ReadStruct(process,
+ reinterpret_cast<WinVMAddress>(
+ reinterpret_cast<const char*>(
+ peb_ldr_data.InMemoryOrderModuleList.Flink) -
+ offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>,
+ InMemoryOrderLinks)),
+ &ldr_data_table_entry)) {
+ return false;
+ }
+ ProcessInfo::Module module;
+ if (!ReadUnicodeString(
+ process, ldr_data_table_entry.FullDllName, &module.name)) {
+ return false;
+ }
+ module.dll_base = ldr_data_table_entry.DllBase;
+ module.size = ldr_data_table_entry.SizeOfImage;
+ module.timestamp = ldr_data_table_entry.TimeDateStamp;
+ process_info->modules_.push_back(module);
+
+ // Walk the PEB LDR structure (doubly-linked list) to get the list of loaded
+ // modules. We use this method rather than EnumProcessModules to get the
+ // modules in initialization order rather than memory order.
+ Traits::Pointer last = peb_ldr_data.InInitializationOrderModuleList.Blink;
+ for (Traits::Pointer cur = peb_ldr_data.InInitializationOrderModuleList.Flink;
+ ;
+ cur = ldr_data_table_entry.InInitializationOrderLinks.Flink) {
+ // |cur| is the pointer to the LIST_ENTRY embedded in the
+ // LDR_DATA_TABLE_ENTRY, in the target process's address space. So we need
+ // to read from the target, and also offset back to the beginning of the
+ // structure.
+ if (!ReadStruct(process,
+ reinterpret_cast<WinVMAddress>(
+ reinterpret_cast<const char*>(cur) -
+ offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>,
+ InInitializationOrderLinks)),
+ &ldr_data_table_entry)) {
+ break;
+ }
+ // TODO(scottmg): Capture Checksum, etc. too?
+ if (!ReadUnicodeString(
+ process, ldr_data_table_entry.FullDllName, &module.name)) {
+ break;
+ }
+ module.dll_base = ldr_data_table_entry.DllBase;
+ module.size = ldr_data_table_entry.SizeOfImage;
+ module.timestamp = ldr_data_table_entry.TimeDateStamp;
+ process_info->modules_.push_back(module);
+ if (cur == last)
+ break;
+ }
+
+ return true;
+}
+
+ProcessInfo::Module::Module() : name(), dll_base(0), size(0), timestamp() {
+}
+
+ProcessInfo::Module::~Module() {
+}
+
+ProcessInfo::ProcessInfo()
+ : process_id_(),
+ inherited_from_process_id_(),
+ command_line_(),
+ modules_(),
+ is_64_bit_(false),
+ is_wow64_(false),
+ initialized_() {
+}
+
+ProcessInfo::~ProcessInfo() {
+}
+
+bool ProcessInfo::Initialize(HANDLE process) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ is_wow64_ = IsProcessWow64(process);
+
+ if (is_wow64_) {
+ // If it's WoW64, then it's 32-on-64.
+ is_64_bit_ = false;
+ } else {
+ // Otherwise, it's either 32 on 32, or 64 on 64. Use GetSystemInfo() to
+ // distinguish between these two cases.
+ SYSTEM_INFO system_info;
+ GetSystemInfo(&system_info);
+ is_64_bit_ =
+ system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
+ }
+
+#if ARCH_CPU_32_BITS
+ if (is_64_bit_) {
+ LOG(ERROR) << "Reading x64 process from x86 process not supported";
+ return false;
+ }
+#endif
+
+ ULONG bytes_returned;
+ // We assume this process is not running on Wow64. The
+ // PROCESS_BASIC_INFORMATION uses the OS size (that is, even Wow64 has a 64
+ // bit one.)
+ // TODO(scottmg): Either support running as Wow64, or check/resolve this at a
+ // higher level.
+#if ARCH_CPU_32_BITS
+ process_types::PROCESS_BASIC_INFORMATION<process_types::internal::Traits32>
+ process_basic_information;
+#else
+ process_types::PROCESS_BASIC_INFORMATION<process_types::internal::Traits64>
+ process_basic_information;
+#endif
+ NTSTATUS status =
+ crashpad::NtQueryInformationProcess(process,
+ ProcessBasicInformation,
+ &process_basic_information,
+ sizeof(process_basic_information),
+ &bytes_returned);
+ if (status < 0) {
+ LOG(ERROR) << "NtQueryInformationProcess: status=" << status;
+ return false;
+ }
+ if (bytes_returned != sizeof(process_basic_information)) {
+ LOG(ERROR) << "NtQueryInformationProcess incorrect size";
+ return false;
+ }
+
+ // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203 on
+ // 32 bit being the correct size for HANDLEs for proceses, even on Windows
+ // x64. API functions (e.g. OpenProcess) take only a DWORD, so there's no
+ // sense in maintaining the top bits.
+ process_id_ = static_cast<DWORD>(process_basic_information.UniqueProcessId);
+ inherited_from_process_id_ = static_cast<DWORD>(
+ process_basic_information.InheritedFromUniqueProcessId);
+
+ // We now want to read the PEB to gather the rest of our information. The
+ // PebBaseAddress as returned above is what we want for 64-on-64 and 32-on-32,
+ // but for Wow64, we want to read the 32 bit PEB (a Wow64 process has both).
+ // The address of this is found by a second call to NtQueryInformationProcess.
+ WinVMAddress peb_address = process_basic_information.PebBaseAddress;
+ if (is_wow64_) {
+ ULONG_PTR wow64_peb_address;
+ status =
+ crashpad::NtQueryInformationProcess(process,
+ ProcessWow64Information,
+ &wow64_peb_address,
+ sizeof(wow64_peb_address),
+ &bytes_returned);
+ if (status < 0) {
+ LOG(ERROR) << "NtQueryInformationProcess: status=" << status;
+ return false;
+ }
+ if (bytes_returned != sizeof(wow64_peb_address)) {
+ LOG(ERROR) << "NtQueryInformationProcess incorrect size";
+ return false;
+ }
+ peb_address = wow64_peb_address;
+ }
+
+ // Read the PEB data using the correct word size.
+ bool result = is_64_bit_ ? ReadProcessData<process_types::internal::Traits64>(
+ process, peb_address, this)
+ : ReadProcessData<process_types::internal::Traits32>(
+ process, peb_address, this);
+ if (!result)
+ return false;
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+bool ProcessInfo::Is64Bit() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return is_64_bit_;
+}
+
+bool ProcessInfo::IsWow64() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return is_wow64_;
+}
+
+pid_t ProcessInfo::ProcessID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return process_id_;
+}
+
+pid_t ProcessInfo::ParentProcessID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return inherited_from_process_id_;
+}
+
+bool ProcessInfo::CommandLine(std::wstring* command_line) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *command_line = command_line_;
+ return true;
+}
+
+bool ProcessInfo::Modules(std::vector<Module>* modules) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *modules = modules_;
+ return true;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/win/process_info.h b/chromium/third_party/crashpad/crashpad/util/win/process_info.h
new file mode 100644
index 00000000000..5d272c42223
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/win/process_info.h
@@ -0,0 +1,107 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_PROCESS_INFO_H_
+#define CRASHPAD_UTIL_WIN_PROCESS_INFO_H_
+
+#include <sys/types.h>
+#include <windows.h>
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "util/misc/initialization_state_dcheck.h"
+#include "util/win/address_types.h"
+
+namespace crashpad {
+
+//! \brief Gathers information about a process given its `HANDLE`. This consists
+//! primarily of information stored in the Process Environment Block.
+class ProcessInfo {
+ public:
+ //! \brief Contains information about a module loaded into a process.
+ struct Module {
+ Module();
+ ~Module();
+
+ //! \brief The pathname used to load the module from disk.
+ std::wstring name;
+
+ //! \brief The base address of the loaded DLL.
+ WinVMAddress dll_base;
+
+ //! \brief The size of the module.
+ WinVMSize size;
+
+ //! \brief The module's timestamp.
+ time_t timestamp;
+ };
+
+ ProcessInfo();
+ ~ProcessInfo();
+
+ //! \brief Initializes this object with information about the given
+ //! \a process.
+ //!
+ //! This method must be called successfully prior to calling any other
+ //! method in this class. This method may only be called once.
+ //!
+ //! \return `true` on success, `false` on failure with a message logged.
+ bool Initialize(HANDLE process);
+
+ //! \return `true` if the target process is a 64-bit process.
+ bool Is64Bit() const;
+
+ //! \return `true` if the target process is running on the Win32-on-Win64
+ //! subsystem.
+ bool IsWow64() const;
+
+ //! \return The target process's process ID.
+ pid_t ProcessID() const;
+
+ //! \return The target process's parent process ID.
+ pid_t ParentProcessID() const;
+
+ //! \return The command line from the target process's Process Environment
+ //! Block.
+ bool CommandLine(std::wstring* command_line) const;
+
+ //! \brief Retrieves the modules loaded into the target process.
+ //!
+ //! The modules are enumerated in initialization order as detailed in the
+ //! Process Environment Block. The main executable will always be the
+ //! first element.
+ bool Modules(std::vector<Module>* modules) const;
+
+ private:
+ template <class T>
+ friend bool ReadProcessData(HANDLE process,
+ WinVMAddress peb_address_vmaddr,
+ ProcessInfo* process_info);
+
+ pid_t process_id_;
+ pid_t inherited_from_process_id_;
+ std::wstring command_line_;
+ std::vector<Module> modules_;
+ bool is_64_bit_;
+ bool is_wow64_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessInfo);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_WIN_PROCESS_INFO_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/win/process_info_test.cc b/chromium/third_party/crashpad/crashpad/util/win/process_info_test.cc
new file mode 100644
index 00000000000..792b3f3e023
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/win/process_info_test.cc
@@ -0,0 +1,172 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/process_info.h"
+
+#include <rpc.h>
+#include <wchar.h>
+
+#include "base/files/file_path.h"
+#include "build/build_config.h"
+#include "gtest/gtest.h"
+#include "test/paths.h"
+#include "util/file/file_io.h"
+#include "util/misc/uuid.h"
+#include "util/win/scoped_handle.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+const wchar_t kNtdllName[] = L"\\ntdll.dll";
+
+time_t GetTimestampForModule(HMODULE module) {
+ wchar_t filename[MAX_PATH];
+ if (!GetModuleFileName(module, filename, arraysize(filename)))
+ return 0;
+ struct _stat stat_buf;
+ int rv = _wstat(filename, &stat_buf);
+ EXPECT_EQ(0, rv);
+ if (rv != 0)
+ return 0;
+ return stat_buf.st_mtime;
+}
+
+TEST(ProcessInfo, Self) {
+ ProcessInfo process_info;
+ ASSERT_TRUE(process_info.Initialize(GetCurrentProcess()));
+ EXPECT_EQ(GetCurrentProcessId(), process_info.ProcessID());
+ EXPECT_GT(process_info.ParentProcessID(), 0u);
+
+#if defined(ARCH_CPU_64_BITS)
+ EXPECT_TRUE(process_info.Is64Bit());
+ EXPECT_FALSE(process_info.IsWow64());
+#else
+ EXPECT_FALSE(process_info.Is64Bit());
+ // Assume we won't be running these tests on a 32 bit host OS.
+ EXPECT_TRUE(process_info.IsWow64());
+#endif
+
+ std::wstring command_line;
+ EXPECT_TRUE(process_info.CommandLine(&command_line));
+ EXPECT_EQ(std::wstring(GetCommandLine()), command_line);
+
+ std::vector<ProcessInfo::Module> modules;
+ EXPECT_TRUE(process_info.Modules(&modules));
+ ASSERT_GE(modules.size(), 2u);
+ const wchar_t kSelfName[] = L"\\crashpad_util_test.exe";
+ ASSERT_GE(modules[0].name.size(), wcslen(kSelfName));
+ EXPECT_EQ(kSelfName,
+ modules[0].name.substr(modules[0].name.size() - wcslen(kSelfName)));
+ ASSERT_GE(modules[1].name.size(), wcslen(kNtdllName));
+ EXPECT_EQ(
+ kNtdllName,
+ modules[1].name.substr(modules[1].name.size() - wcslen(kNtdllName)));
+
+ EXPECT_EQ(modules[0].dll_base,
+ reinterpret_cast<uintptr_t>(GetModuleHandle(nullptr)));
+ EXPECT_EQ(modules[1].dll_base,
+ reinterpret_cast<uintptr_t>(GetModuleHandle(L"ntdll.dll")));
+
+ EXPECT_GT(modules[0].size, 0);
+ EXPECT_GT(modules[1].size, 0);
+
+ EXPECT_EQ(modules[0].timestamp,
+ GetTimestampForModule(GetModuleHandle(nullptr)));
+ // System modules are forced to particular stamps and the file header values
+ // don't match the on-disk times. Just make sure we got some data here.
+ EXPECT_GT(modules[1].timestamp, 0);
+}
+
+void TestOtherProcess(const std::wstring& child_name_suffix) {
+ ProcessInfo process_info;
+
+ ::UUID system_uuid;
+ ASSERT_EQ(RPC_S_OK, UuidCreate(&system_uuid));
+ UUID started_uuid(reinterpret_cast<const uint8_t*>(&system_uuid.Data1));
+ ASSERT_EQ(RPC_S_OK, UuidCreate(&system_uuid));
+ UUID done_uuid(reinterpret_cast<const uint8_t*>(&system_uuid.Data1));
+
+ ScopedKernelHANDLE started(
+ CreateEvent(nullptr, true, false, started_uuid.ToString16().c_str()));
+ ASSERT_TRUE(started.get());
+ ScopedKernelHANDLE done(
+ CreateEvent(nullptr, true, false, done_uuid.ToString16().c_str()));
+ ASSERT_TRUE(done.get());
+
+ base::FilePath test_executable = Paths::Executable();
+ std::wstring child_test_executable =
+ test_executable.RemoveFinalExtension().value() +
+ L"_process_info_test_child_" + child_name_suffix + L".exe";
+ // TODO(scottmg): Command line escaping utility.
+ std::wstring command_line = child_test_executable + L" " +
+ started_uuid.ToString16() + L" " +
+ done_uuid.ToString16();
+ STARTUPINFO startup_info = {0};
+ startup_info.cb = sizeof(startup_info);
+ PROCESS_INFORMATION process_information;
+ ASSERT_TRUE(CreateProcess(child_test_executable.c_str(),
+ &command_line[0],
+ nullptr,
+ nullptr,
+ false,
+ 0,
+ nullptr,
+ nullptr,
+ &startup_info,
+ &process_information));
+ // Take ownership of the two process handles returned.
+ ScopedKernelHANDLE process_main_thread_handle(process_information.hThread);
+ ScopedKernelHANDLE process_handle(process_information.hProcess);
+
+ // Wait until the test has completed initialization.
+ ASSERT_EQ(WaitForSingleObject(started.get(), INFINITE), WAIT_OBJECT_0);
+
+ ASSERT_TRUE(process_info.Initialize(process_information.hProcess));
+
+ // Tell the test it's OK to shut down now that we've read our data.
+ SetEvent(done.get());
+
+ std::vector<ProcessInfo::Module> modules;
+ EXPECT_TRUE(process_info.Modules(&modules));
+ ASSERT_GE(modules.size(), 3u);
+ std::wstring child_name = L"\\crashpad_util_test_process_info_test_child_" +
+ child_name_suffix + L".exe";
+ ASSERT_GE(modules[0].name.size(), child_name.size());
+ EXPECT_EQ(child_name,
+ modules[0].name.substr(modules[0].name.size() - child_name.size()));
+ ASSERT_GE(modules[1].name.size(), wcslen(kNtdllName));
+ EXPECT_EQ(
+ kNtdllName,
+ modules[1].name.substr(modules[1].name.size() - wcslen(kNtdllName)));
+ // lz32.dll is an uncommonly-used-but-always-available module that the test
+ // binary manually loads.
+ const wchar_t kLz32dllName[] = L"\\lz32.dll";
+ ASSERT_GE(modules.back().name.size(), wcslen(kLz32dllName));
+ EXPECT_EQ(kLz32dllName,
+ modules.back().name.substr(modules.back().name.size() -
+ wcslen(kLz32dllName)));
+}
+
+TEST(ProcessInfo, OtherProcessX64) {
+ TestOtherProcess(L"x64");
+}
+
+TEST(ProcessInfo, OtherProcessX86) {
+ TestOtherProcess(L"x86");
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/win/process_info_test_child.cc b/chromium/third_party/crashpad/crashpad/util/win/process_info_test_child.cc
new file mode 100644
index 00000000000..a0cf5c403a5
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/win/process_info_test_child.cc
@@ -0,0 +1,45 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdlib.h>
+#include <wchar.h>
+#include <windows.h>
+
+// A simple binary to be loaded and inspected by ProcessInfo.
+int wmain(int argc, wchar_t** argv) {
+ if (argc != 3)
+ abort();
+
+ // Get handles to the events we use to communicate with our parent.
+ HANDLE started_event = CreateEvent(nullptr, true, false, argv[1]);
+ HANDLE done_event = CreateEvent(nullptr, true, false, argv[2]);
+ if (!started_event || !done_event)
+ abort();
+
+ // Load an unusual module (that we don't depend upon) so we can do an
+ // existence check.
+ if (!LoadLibrary(L"lz32.dll"))
+ abort();
+
+ if (!SetEvent(started_event))
+ abort();
+
+ if (WaitForSingleObject(done_event, INFINITE) != WAIT_OBJECT_0)
+ abort();
+
+ CloseHandle(done_event);
+ CloseHandle(started_event);
+
+ return EXIT_SUCCESS;
+}
diff --git a/chromium/third_party/crashpad/crashpad/util/win/process_structs.h b/chromium/third_party/crashpad/crashpad/util/win/process_structs.h
new file mode 100644
index 00000000000..ece6089601c
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/win/process_structs.h
@@ -0,0 +1,291 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_PROCESS_STRUCTS_H_
+#define CRASHPAD_UTIL_WIN_PROCESS_STRUCTS_H_
+
+#include <windows.h>
+
+namespace crashpad {
+namespace process_types {
+
+namespace internal {
+
+struct Traits32 {
+ using Pad = DWORD;
+ using UnsignedIntegral = DWORD;
+ using Pointer = DWORD;
+};
+
+struct Traits64 {
+ using Pad = DWORD64;
+ using UnsignedIntegral = DWORD64;
+ using Pointer = DWORD64;
+};
+
+} // namespace internal
+
+//! \{
+
+//! \brief Selected structures from winternl.h, ntddk.h, and `dt ntdll!xxx`,
+//! customized to have both x86 and x64 sizes available.
+//!
+//! The structure and field names follow the Windows names for clarity. We do,
+//! however, use plain integral types rather than pointer types. This is both
+//! easier to define, and avoids accidentally treating them as pointers into the
+//! current address space.
+//!
+//! The templates below should be instantiated with either internal::Traits32
+//! for structures targeting x86, or internal::Traits64 for x64.
+
+// We set packing to 1 so that we can explicitly control the layout to make it
+// match the OS defined structures.
+#pragma pack(push, 1)
+
+template <class Traits>
+struct PROCESS_BASIC_INFORMATION {
+ union {
+ DWORD ExitStatus;
+ typename Traits::Pad padding_for_x64_0;
+ };
+ typename Traits::Pointer PebBaseAddress;
+ typename Traits::UnsignedIntegral AffinityMask;
+ union {
+ DWORD BasePriority;
+ typename Traits::Pad padding_for_x64_1;
+ };
+ typename Traits::UnsignedIntegral UniqueProcessId;
+ typename Traits::UnsignedIntegral InheritedFromUniqueProcessId;
+};
+
+template <class Traits>
+struct LIST_ENTRY {
+ typename Traits::Pointer Flink;
+ typename Traits::Pointer Blink;
+};
+
+template <class Traits>
+struct UNICODE_STRING {
+ union {
+ struct {
+ USHORT Length;
+ USHORT MaximumLength;
+ };
+ typename Traits::Pad padding_for_x64;
+ };
+ typename Traits::Pointer Buffer;
+};
+
+template <class Traits>
+struct PEB_LDR_DATA {
+ ULONG Length;
+ DWORD Initialized;
+ typename Traits::Pointer SsHandle;
+ LIST_ENTRY<Traits> InLoadOrderModuleList;
+ LIST_ENTRY<Traits> InMemoryOrderModuleList;
+ LIST_ENTRY<Traits> InInitializationOrderModuleList;
+};
+
+template <class Traits>
+struct LDR_DATA_TABLE_ENTRY {
+ LIST_ENTRY<Traits> InLoadOrderLinks;
+ LIST_ENTRY<Traits> InMemoryOrderLinks;
+ LIST_ENTRY<Traits> InInitializationOrderLinks;
+ typename Traits::Pointer DllBase;
+ typename Traits::Pointer EntryPoint;
+ union {
+ ULONG SizeOfImage;
+ typename Traits::Pad padding_for_x64;
+ };
+ UNICODE_STRING<Traits> FullDllName;
+ UNICODE_STRING<Traits> BaseDllName;
+ ULONG Flags;
+ USHORT ObsoleteLoadCount;
+ USHORT TlsIndex;
+ LIST_ENTRY<Traits> HashLinks;
+ ULONG TimeDateStamp;
+};
+
+template <class Traits>
+struct CURDIR {
+ UNICODE_STRING<Traits> DosPath;
+ typename Traits::Pointer Handle;
+};
+
+template <class Traits>
+struct STRING {
+ union {
+ struct {
+ DWORD Length;
+ DWORD MaximumLength;
+ };
+ typename Traits::Pad padding_for_x64;
+ };
+ typename Traits::Pointer Buffer;
+};
+
+template <class Traits>
+struct RTL_DRIVE_LETTER_CURDIR {
+ WORD Flags;
+ WORD Length;
+ DWORD TimeStamp;
+ STRING<Traits> DosPath;
+};
+
+template <class Traits>
+struct RTL_USER_PROCESS_PARAMETERS {
+ DWORD MaximumLength;
+ DWORD Length;
+ DWORD Flags;
+ DWORD DebugFlags;
+ typename Traits::Pointer ConsoleHandle;
+ union {
+ DWORD ConsoleFlags;
+ typename Traits::Pad padding_for_x64;
+ };
+ typename Traits::Pointer StandardInput;
+ typename Traits::Pointer StandardOutput;
+ typename Traits::Pointer StandardError;
+ CURDIR<Traits> CurrentDirectory;
+ UNICODE_STRING<Traits> DllPath;
+ UNICODE_STRING<Traits> ImagePathName;
+ UNICODE_STRING<Traits> CommandLine;
+ typename Traits::Pointer Environment;
+ DWORD StartingX;
+ DWORD StartingY;
+ DWORD CountX;
+ DWORD CountY;
+ DWORD CountCharsX;
+ DWORD CountCharsY;
+ DWORD FillAttribute;
+ DWORD WindowFlags;
+ DWORD ShowWindowFlags;
+ UNICODE_STRING<Traits> WindowTitle;
+ UNICODE_STRING<Traits> DesktopInfo;
+ UNICODE_STRING<Traits> ShellInfo;
+ UNICODE_STRING<Traits> RuntimeData;
+ RTL_DRIVE_LETTER_CURDIR<Traits> CurrentDirectores[32]; // sic.
+};
+
+template <class T>
+struct GdiHandleBufferCountForBitness;
+
+template <>
+struct GdiHandleBufferCountForBitness<internal::Traits32> {
+ enum { value = 34 };
+};
+template <>
+struct GdiHandleBufferCountForBitness<internal::Traits64> {
+ enum { value = 60 };
+};
+
+template <class Traits>
+struct PEB {
+ union {
+ struct {
+ BYTE InheritedAddressSpace;
+ BYTE ReadImageFileExecOptions;
+ BYTE BeingDebugged;
+ BYTE BitField;
+ };
+ typename Traits::Pad padding_for_x64_0;
+ };
+ typename Traits::Pointer Mutant;
+ typename Traits::Pointer ImageBaseAddress;
+ typename Traits::Pointer Ldr;
+ typename Traits::Pointer ProcessParameters;
+ typename Traits::Pointer SubSystemData;
+ typename Traits::Pointer ProcessHeap;
+ typename Traits::Pointer FastPebLock;
+ typename Traits::Pointer AtlThunkSListPtr;
+ typename Traits::Pointer IFEOKey;
+ union {
+ DWORD CrossProcessFlags;
+ typename Traits::Pad padding_for_x64_1;
+ };
+ typename Traits::Pointer KernelCallbackTable;
+ DWORD SystemReserved;
+ DWORD AtlThunkSListPtr32;
+ typename Traits::Pointer ApiSetMap;
+ union {
+ DWORD TlsExpansionCounter;
+ typename Traits::Pad padding_for_x64_2;
+ };
+ typename Traits::Pointer TlsBitmap;
+ DWORD TlsBitmapBits[2];
+ typename Traits::Pointer ReadOnlySharedMemoryBase;
+ typename Traits::Pointer SparePvoid0;
+ typename Traits::Pointer ReadOnlyStaticServerData;
+ typename Traits::Pointer AnsiCodePageData;
+ typename Traits::Pointer OemCodePageData;
+ typename Traits::Pointer UnicodeCaseTableData;
+ DWORD NumberOfProcessors;
+ DWORD NtGlobalFlag;
+ LARGE_INTEGER CriticalSectionTimeout;
+ typename Traits::UnsignedIntegral HeapSegmentReserve;
+ typename Traits::UnsignedIntegral HeapSegmentCommit;
+ typename Traits::UnsignedIntegral HeapDeCommitTotalFreeThreshold;
+ typename Traits::UnsignedIntegral HeapDeCommitFreeBlockThreshold;
+ DWORD NumberOfHeaps;
+ DWORD MaximumNumberOfHeaps;
+ typename Traits::Pointer ProcessHeaps;
+ typename Traits::Pointer GdiSharedHandleTable;
+ typename Traits::Pointer ProcessStarterHelper;
+ DWORD GdiDCAttributeList;
+ typename Traits::Pointer LoaderLock;
+ DWORD OSMajorVersion;
+ DWORD OSMinorVersion;
+ WORD OSBuildNumber;
+ WORD OSCSDVersion;
+ DWORD OSPlatformId;
+ DWORD ImageSubsystem;
+ DWORD ImageSubsystemMajorVersion;
+ union {
+ DWORD ImageSubsystemMinorVersion;
+ typename Traits::Pad padding_for_x64_3;
+ };
+ typename Traits::UnsignedIntegral ActiveProcessAffinityMask;
+ DWORD GdiHandleBuffer[GdiHandleBufferCountForBitness<Traits>::value];
+ typename Traits::Pointer PostProcessInitRoutine;
+ typename Traits::Pointer TlsExpansionBitmap;
+ DWORD TlsExpansionBitmapBits[32];
+ union {
+ DWORD SessionId;
+ typename Traits::Pad padding_for_x64_4;
+ };
+ ULARGE_INTEGER AppCompatFlags;
+ ULARGE_INTEGER AppCompatFlagsUser;
+ typename Traits::Pointer pShimData;
+ typename Traits::Pointer AppCompatInfo;
+ UNICODE_STRING<Traits> CSDVersion;
+ typename Traits::Pointer ActivationContextData;
+ typename Traits::Pointer ProcessAssemblyStorageMap;
+ typename Traits::Pointer SystemDefaultActivationContextData;
+ typename Traits::Pointer SystemAssemblyStorageMap;
+ typename Traits::UnsignedIntegral MinimumStackCommit;
+ typename Traits::Pointer FlsCallback;
+ LIST_ENTRY<Traits> FlsListHead;
+ typename Traits::Pointer FlsBitmap;
+ DWORD FlsBitmapBits[4];
+ DWORD FlsHighIndex;
+};
+
+#pragma pack(pop)
+
+//! \}
+
+} // namespace process_types
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_WIN_PROCESS_STRUCTS_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/win/scoped_handle.cc b/chromium/third_party/crashpad/crashpad/util/win/scoped_handle.cc
new file mode 100644
index 00000000000..5eb440e045d
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/win/scoped_handle.cc
@@ -0,0 +1,32 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/scoped_handle.h"
+
+#include "base/logging.h"
+#include "util/file/file_io.h"
+
+namespace crashpad {
+namespace internal {
+
+void ScopedFileHANDLECloseTraits::Free(HANDLE handle) {
+ CheckedCloseFile(handle);
+}
+
+void ScopedKernelHANDLECloseTraits::Free(HANDLE handle) {
+ PCHECK(CloseHandle(handle));
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/win/scoped_handle.h b/chromium/third_party/crashpad/crashpad/util/win/scoped_handle.h
new file mode 100644
index 00000000000..68be4f7b9f9
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/win/scoped_handle.h
@@ -0,0 +1,49 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_SCOPED_HANDLE_H_
+#define CRASHPAD_UTIL_WIN_SCOPED_HANDLE_H_
+
+#include <windows.h>
+
+#include "base/scoped_generic.h"
+
+namespace crashpad {
+
+namespace internal {
+
+struct ScopedFileHANDLECloseTraits {
+ static HANDLE InvalidValue() {
+ return INVALID_HANDLE_VALUE;
+ }
+ static void Free(HANDLE handle);
+};
+
+struct ScopedKernelHANDLECloseTraits {
+ static HANDLE InvalidValue() {
+ return nullptr;
+ }
+ static void Free(HANDLE handle);
+};
+
+} // namespace internal
+
+using ScopedFileHANDLE =
+ base::ScopedGeneric<HANDLE, internal::ScopedFileHANDLECloseTraits>;
+using ScopedKernelHANDLE =
+ base::ScopedGeneric<HANDLE, internal::ScopedKernelHANDLECloseTraits>;
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_WIN_SCOPED_HANDLE_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/win/time.cc b/chromium/third_party/crashpad/crashpad/util/win/time.cc
new file mode 100644
index 00000000000..1665b9c6efb
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/win/time.cc
@@ -0,0 +1,65 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/time.h"
+
+#include <inttypes.h>
+
+#include "base/logging.h"
+
+namespace crashpad {
+
+namespace {
+
+const uint64_t kMicrosecondsPerSecond = static_cast<uint64_t>(1E6);
+
+uint64_t FiletimeToMicroseconds(const FILETIME& filetime) {
+ uint64_t t = (static_cast<uint64_t>(filetime.dwHighDateTime) << 32) |
+ filetime.dwLowDateTime;
+ return t / 10; // 100 nanosecond intervals to microseconds.
+}
+
+timeval MicrosecondsToTimeval(uint64_t microseconds) {
+ timeval tv;
+ tv.tv_sec = static_cast<long>(microseconds / kMicrosecondsPerSecond);
+ tv.tv_usec = static_cast<long>(microseconds % kMicrosecondsPerSecond);
+ return tv;
+}
+
+} // namespace
+
+timeval FiletimeToTimevalEpoch(const FILETIME& filetime) {
+ uint64_t microseconds = FiletimeToMicroseconds(filetime);
+
+ // Windows epoch is 1601-01-01, and FILETIME ticks are 100 nanoseconds.
+ // 1601 to 1970 is 369 years + 89 leap days = 134774 days * 86400 seconds per
+ // day. It's not entirely clear, but it appears that these are solar seconds,
+ // not SI seconds, so there are no leap seconds to be considered.
+ const uint64_t kNumSecondsFrom1601To1970 = (369 * 365 + 89) * 86400ULL;
+ DCHECK_GE(microseconds, kNumSecondsFrom1601To1970 * kMicrosecondsPerSecond);
+ microseconds -= kNumSecondsFrom1601To1970 * kMicrosecondsPerSecond;
+ return MicrosecondsToTimeval(microseconds);
+}
+
+timeval FiletimeToTimevalInterval(const FILETIME& filetime) {
+ return MicrosecondsToTimeval(FiletimeToMicroseconds(filetime));
+}
+
+void GetTimeOfDay(timeval* tv) {
+ FILETIME filetime;
+ GetSystemTimeAsFileTime(&filetime);
+ *tv = FiletimeToTimevalEpoch(filetime);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/win/time.h b/chromium/third_party/crashpad/crashpad/util/win/time.h
new file mode 100644
index 00000000000..7cc0094f35e
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/win/time.h
@@ -0,0 +1,36 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_TIME_H_
+#define CRASHPAD_UTIL_WIN_TIME_H_
+
+#include <sys/time.h>
+#include <windows.h>
+
+namespace crashpad {
+
+//! \brief Convert Windows `FILETIME` to `timeval`, converting from Windows
+//! epoch to POSIX epoch.
+timeval FiletimeToTimevalEpoch(const FILETIME& filetime);
+
+//! \brief Convert Windows `FILETIME` to `timeval`, treating the values as
+//! an interval of elapsed time.
+timeval FiletimeToTimevalInterval(const FILETIME& filetime);
+
+//! \brief Similar to POSIX gettimeofday(), gets the current system time in UTC.
+void GetTimeOfDay(timeval* tv);
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_WIN_TIME_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/win/time_test.cc b/chromium/third_party/crashpad/crashpad/util/win/time_test.cc
new file mode 100644
index 00000000000..ad0771e38b7
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/win/time_test.cc
@@ -0,0 +1,34 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/time.h"
+
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(Time, Reasonable) {
+ timeval t;
+ GetTimeOfDay(&t);
+ // Assume that time's time_t return is seconds from 1970.
+ time_t approx_now = time(nullptr);
+ EXPECT_GE(approx_now, t.tv_sec);
+ EXPECT_LT(approx_now - 100, t.tv_sec);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad